Class: Delivery
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Delivery
- Includes:
- Memery, Models::Auditable, Models::LegacyRateRequest, Models::Md5Hashable, 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)
Defined Under Namespace
Classes: InvoicingHandler
Constant Summary collapse
- SHIPPING_STATES =
%i[at_warehouse picking pending_pickup_confirm pending_ship_labels pending_carrier_confirm pending_ship_confirm shipped].freeze
- ANY_EMPLOYEE_CANCELABLE_STATES =
%i[quoting awaiting_po_fulfillment at_warehouse future_release service_ready_to_fulfill return_labels_complete].freeze
- WAREHOUSE_CANCELABLE_STATES =
%i[pre_pack picking pending_pickup_confirm pending_ship_labels].freeze
- WAREHOUSE_STATES =
%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 =
(ANY_EMPLOYEE_CANCELABLE_STATES + WAREHOUSE_CANCELABLE_STATES).uniq
- CHECK_COLD_LEAD_NOTE =
'Check notes, contains COLD LEAD.'- SHIP_LABEL_HOLD_PERCENT_THRESHOLD =
25.0- SHIP_LABEL_HOLD_DOLLAR_THRESHOLD =
25.0- SHIP_LABEL_HOLD_WEIGHT_PERCENT_THRESHOLD =
15.0- SHIP_LABEL_HOLD_WEIGHT_THRESHOLD =
7.5- CARRIERS_REQUIRING_MANIFEST_COMPLETION =
['SpeedeeDelivery'].freeze
- CROSS_BORDER_BROKER_INSTRUCTIONS =
'Broker: Willson International, Email: service@willsonintl.com'- CROSS_BORDER_COUNTRY_SPECIFIC_BROKER_TEXT =
{ 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 =
[ { name: 'RlCarriers', customs_email: 'transbordersolutiongroup@rlcarriers.com' }, { name: 'Freightquote' } ]
- CARRIERS_NAMES_TO_SEND_COMMERCIAL_INVOICES =
CARRIERS_TO_SEND_COMMERCIAL_INVOICES.map{|carr| carr[:name]}
- FREIGHTQUOTE_CARRIERS_TO_SEND_COMMERCIAL_INVOICES =
[ { 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 =
20.0- FALLBACK_PER_LB_OVERRIDE_COST_WWW =
5.0- FALLBACK_MAX_OVERRIDE_COST_FRACTION_WWW =
20.0- FALLBACK_OVERRIDE_COST_CRM =
500.0
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
-
#do_not_validate_line_items ⇒ Object
Returns the value of attribute do_not_validate_line_items.
-
#force_shipping_cost_update ⇒ Object
Returns the value of attribute force_shipping_cost_update.
-
#override_carrier ⇒ Object
Returns the value of attribute override_carrier.
-
#override_future_release_date ⇒ Object
Returns the value of attribute override_future_release_date.
-
#payment_ids ⇒ Object
Returns the value of attribute payment_ids.
Attributes included from Models::Profitable
Belongs to collapse
- #destination_address ⇒ Address
- #order ⇒ Order
- #origin_address ⇒ Address
- #prepack_requester ⇒ Party
- #quote ⇒ Quote
- #selected_shipping_cost ⇒ ShippingCost
- #shipping_account_number ⇒ ShippingAccountNumber
- #shipping_option ⇒ ShippingOption
- #supplier ⇒ Supplier
Methods included from Models::Auditable
Has one collapse
Has many collapse
- #activities ⇒ ActiveRecord::Relation<Activity>
- #discounts ⇒ ActiveRecord::Relation<Discount>
- #drop_ship_purchase_orders ⇒ ActiveRecord::Relation<PurchaseOrder>
- #invoices ⇒ ActiveRecord::Relation<Invoice>
- #item_ledger_entries ⇒ ActiveRecord::Relation<ItemLedgerEntry>
- #ledger_transactions ⇒ ActiveRecord::Relation<LedgerTransaction>
- #line_discounts ⇒ ActiveRecord::Relation<LineDiscount>
- #line_items ⇒ ActiveRecord::Relation<LineItem>
- #messaging_logs ⇒ ActiveRecord::Relation<MessagingLog>
- #preset_jobs ⇒ ActiveRecord::Relation<PresetJob>
- #reserved_serial_numbers ⇒ ActiveRecord::Relation<ReservedSerialNumber>
- #shipments ⇒ ActiveRecord::Relation<Shipment>
-
#shipping_costs ⇒ ActiveRecord::Relation<ShippingCost>
dependent destroy handled by trigger.
- #uploads ⇒ ActiveRecord::Relation<Upload>
Methods included from Models::Payable
Delegated Instance Attributes collapse
-
#billing_entity ⇒ Object
Alias for Resource#billing_entity.
-
#catalog ⇒ Object
Alias for Customer#catalog.
-
#customer ⇒ Object
Alias for Resource_or_rma_for_delivery#customer.
-
#is_amazon_seller_central? ⇒ Object
Alias for Customer#is_amazon_seller_central?.
-
#po_number ⇒ Object
Alias for Order#po_number.
-
#primary_party ⇒ Object
Alias for Resource_or_rma_for_delivery#primary_party.
-
#store ⇒ Object
Alias for Resource#store.
Class Method Summary collapse
-
.active ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are active.
-
.all_at_warehouse ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are all at warehouse.
- .auto_ship_confirm(logger: nil) ⇒ Object
-
.awaiting_po_fulfillment ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are awaiting po fulfillment.
-
.by_order_store_id ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are by order store id.
-
.by_quote_store_id ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are by quote store id.
-
.by_store_id ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are by store id.
-
.cancelable ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are cancelable.
-
.dropship ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are dropship.
-
.fedex_express ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are fedex express.
-
.fedex_ground ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are fedex ground.
-
.for_future_release ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are for future release.
- .generate_super_pick_slip_pdf(deliveries, split_kits = false) ⇒ Object
- .invoice_shipped_deliveries(_logger = Rails.logger) ⇒ Object
-
.invoiced ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are invoiced.
-
.limit_to_fba ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are limit to fba.
-
.non_pickups ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are non pickups.
-
.non_quoting ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are non quoting.
-
.not_cancelable ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are not cancelable.
-
.pending_manifest_completion ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are pending manifest completion.
-
.pending_ship_confirm ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are pending ship confirm.
-
.pickups ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are pickups.
-
.processing_po_fulfillment ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are processing po fulfillment.
-
.quoting ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are quoting.
-
.quoting_or_pre_pack ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are quoting or pre pack.
- .release_deliveries_past_release_date ⇒ Object
-
.sales_orders ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are sales orders.
- .send_manual_release_due_notification ⇒ Object
-
.ship_labeled_before ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are ship labeled before.
-
.shipped ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are shipped.
-
.shipping ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are shipping.
- .states_for_select ⇒ Object
-
.with_active_parent ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are with active parent.
-
.with_associations ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are with associations.
-
.with_line_items ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are with line items.
-
.with_shipment_carrier ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are with shipment carrier.
Instance Method Summary collapse
- #actual_shipping_cost_exceeds_threshold? ⇒ Boolean
- #actual_shipping_cost_to_show ⇒ Object
- #actual_shipping_cost_within_threshold? ⇒ Boolean
- #actual_weight ⇒ Object (also: #actual_shipment_weight)
- #actual_weight_discrepancy_exceeds_threshold? ⇒ Boolean
- #add_or_update_purchase_order_item(po, existing_po_items, item, li, unit_cost) ⇒ Object
- #add_purchase_order_items(po, item, grouped_line_items, existing_po_items) ⇒ Object
- #adjusted_actual_shipping_cost ⇒ Object
- #all_activities ⇒ Object
- #all_dropship_items_fulfilled? ⇒ Boolean
- #all_intl_forms_pdf ⇒ Object
- #all_labels_pdf ⇒ Object
- #all_lines_allocated_to_shipments? ⇒ Boolean
- #all_lines_allocated_to_shipments_and_shipments_have_weight? ⇒ Boolean
- #all_shipments_weights_match_expected ⇒ Object
- #apply_cheapest_economy_shipping_method ⇒ Object
-
#apply_selected_shipping_cost!(shipping_cost_entry, previous_selected_id: nil, persist: false) ⇒ Object
Centralized application of the selected shipping cost to delivery and its shipping line Ensures consistency whether selection is auto-computed or explicitly chosen elsewhere.
- #apply_shipping_match_for_economy_shipping ⇒ Object
- #apply_warehouse_fee? ⇒ Boolean
- #at_least_one_shipment ⇒ Object
-
#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).
- #bol_pdf ⇒ Object
- #build_new_purchase_order_item(item, li, quantity, unit_cost) ⇒ Object
- #build_purchase_order(supplier) ⇒ Object
- #calculate_all_cogs ⇒ Object
- #calculate_declared_value ⇒ Object
- #calculate_grand_total ⇒ Object
- #calculate_shipping_options(options = {}) ⇒ Object
- #can_be_deleted? ⇒ Boolean
- #can_print_carton_labels? ⇒ Boolean
- #can_update_tracking_info? ⇒ Boolean
- #can_void_rma_delivery? ⇒ Boolean
-
#canadian_tire_special_check? ⇒ Boolean
Canadian tire requires all packages are less than 67 lbs to ship Purolator otherwise Consolidated Fastfrate LTL.
- #cancel ⇒ Object
- #cancel_estimated_packaging ⇒ Object
-
#cancelable?(current_user = nil) ⇒ Boolean
These methods above are from the model previously known as delivery_quote.
-
#cannot_ship_empty_delivery? ⇒ Boolean
Guard method for shipping state transitions.
- #carrier_customs_email ⇒ Object
- #carrier_icon ⇒ Object
- #carrier_options_for_select ⇒ Object
- #chosen_shipping_method ⇒ Object
- #chosen_shipping_method_carrier_cost ⇒ Object
- #commit_catalog_items ⇒ Object
- #commit_reserved_serial_numbers ⇒ Object
- #complete_picked ⇒ Object
- #completed_regular_delivery? ⇒ Boolean
-
#copy_shipments_if_drop_ship_po ⇒ Object
Copies shipments from associated drop ship purchase orders to this delivery's shipments.
- #country ⇒ Object
- #create_invoice ⇒ Object
- #create_shipments_from_equivalent_delivery(equivalent_delivery, shipment_state = 'awaiting_label') ⇒ Object
- #create_st_ledger_entries ⇒ Object
- #currency ⇒ Object
- #currency_symbol ⇒ Object
- #custom_pack_list ⇒ Object
- #default_container_type ⇒ Object
- #delete_serial_number_reservations ⇒ Object
- #delta_weight_factor_remaining_to_allocate ⇒ Object
-
#display_carrier_name ⇒ Object
When Freightquote is used as a broker, the human-readable carrier is stored in rate_data rather than on the delivery itself.
- #do_not_ship_insure_via_carrier? ⇒ Boolean
- #do_not_validate_line_items? ⇒ Boolean
- #does_not_require_shipping_labeling? ⇒ Boolean
- #electronic_ship_ci_pdf ⇒ Object
-
#estimated_delivery_date ⇒ Object
Trying to 'guess' the delivery date.
- #european_shipment? ⇒ Boolean
- #existing_shipment_attributes=(shipment_attributes) ⇒ Object
- #existing_shipping_cost_attributes=(shipping_cost_attributes) ⇒ Object
- #freightquote_carrier? ⇒ Boolean
- #friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false) ⇒ Object
- #friendly_shipping_method_for_edi ⇒ Object
- #generate_all_international_forms_pdf ⇒ Object
- #generate_all_labels_pdf ⇒ Object
-
#generate_asynch_labels ⇒ Object
ScoutAPM Disabled instrument_method :generate_labels.
- #generate_barcode ⇒ Object
- #generate_bol_pdf(ship_bol_tmp_path = nil) ⇒ Object
- #generate_ci_pdf(ci_tmp_path = nil) ⇒ Object
-
#generate_combined_pdf(split_kits: false, skip_plans: false) ⇒ Object
this must be here, even if just as a wrapper because the method above calls it!.
-
#generate_dropship_po ⇒ Object
Generates dropship purchase orders and purchase order items for any dropship line items on this delivery that do not already have associated purchase orders.
-
#generate_labels ⇒ Object
res = uploads << upload end res end.
- #generate_pick_slip_pdf(split_kits = false) ⇒ Object
- #generate_serial_numbers_pdf ⇒ Object
- #get_address_hash_from_address(address) ⇒ Object
- #get_economy_shipping_cost_to_use ⇒ Object
- #get_existing_po_items ⇒ Object
- #get_fallback_cost_to_use ⇒ Object
- #get_or_generate_pick_slip_pdf(split_kits = false) ⇒ Object
- #group_line_items_by_item(line_items) ⇒ Object
- #group_unprocessed_dropship_line_items_by_supplier ⇒ Object
- #has_custom_products? ⇒ Boolean
- #has_custom_shipping_labels? ⇒ Boolean
- #has_destination_postal_code ⇒ Object
- #has_dropship_items? ⇒ Boolean
- #has_future_release_date? ⇒ Boolean
- #has_kits? ⇒ Boolean
- #has_kits_or_serial_numbers? ⇒ Boolean
- #has_not_changed_but_has_shipping_lines? ⇒ Boolean
- #has_ready_to_print_amazon_fba_items? ⇒ Boolean
- #has_serial_numbers? ⇒ Boolean
-
#has_shippable_content? ⇒ Boolean
Check if this delivery has shippable content (non-shipping line items).
- #has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line) ⇒ Boolean
- #has_unfulfilled_dropship_items? ⇒ Boolean
- #has_unprocessed_dropship_items? ⇒ Boolean
- #has_valid_shipments? ⇒ Boolean
- #incurs_oversized_penalty? ⇒ Boolean
- #index ⇒ Object
-
#individual_auto_ship_confirm(logger: nil) ⇒ Object
Transitions a delivery to shipped state if ready.
- #instant_quote? ⇒ Boolean
- #instantiate_shipping_insurance ⇒ Object
- #insured_shipments ⇒ Object
- #insured_value ⇒ Object
- #is_amazon_seller_central_veeqo? ⇒ Boolean
- #is_cross_border? ⇒ Boolean
- #is_default_ltl_freight? ⇒ Boolean
- #is_domestic? ⇒ Boolean
- #is_international? ⇒ Boolean
- #is_international_and_ups_or_fedex? ⇒ Boolean
- #is_part_of_manifest? ⇒ Boolean
- #is_rma_return ⇒ Object (also: #is_rma_return?)
- #is_smart_service? ⇒ Boolean
-
#is_sww_shipping_cost?(sc = nil) ⇒ Boolean
Check if the shipping cost is a Ship with Walmart rate.
- #is_www? ⇒ Boolean
- #line_allocation_status_hash ⇒ Object
- #line_items_eligible_for_packing ⇒ Object
- #line_items_requiring_serial_number ⇒ Object
- #line_items_with_counters ⇒ Object
-
#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.
- #link_serial_numbers_to_line_items ⇒ Object
- #linked_return_deliveries ⇒ Object
- #locked_for_fba? ⇒ Boolean
- #ltl_freight_has_changed? ⇒ Boolean
- #mark_multi_shipments_manifested(manifest, shipment) ⇒ Object
- #matches?(existing_item, item, quantity) ⇒ Boolean
- #name(short = false, filename = false) ⇒ Object
- #new_shipment_attributes=(shipment_attributes) ⇒ Object
- #no_empty_shipments ⇒ Object
- #notify_store ⇒ Object
- #open_activities_counter ⇒ Object
- #override_shipping_method? ⇒ Boolean
- #packable_active_lines ⇒ Object
- #packable_active_parent_lines_only(skip_spare_parts = false) ⇒ Object
- #packable_item_hash ⇒ Object
- #packable_parent_lines_item_hash(skip_spare_parts = false) ⇒ Object
-
#packageable? ⇒ Boolean
Are there shipments that can have content specified?.
- #pallet_weight_matching ⇒ Object
- #pick_slip_file_name(with_extension = true) ⇒ Object
-
#pick_slip_line_items(split_kits: false, sort_method: :location) ⇒ Object
Generates a line hash for the pick/pack slip pdf.
-
#preferred_shipping_option ⇒ Object
These methods below are from the model previously known as delivery_quote.
- #print_container_label? ⇒ Boolean
- #quantities_remaining_to_allocate ⇒ Object
- #ready_to_choose_ships_economy_carrier? ⇒ Boolean
- #ready_to_print_amazon_fba_line_items ⇒ Object
- #ready_to_ship! ⇒ Object
- #reference_number ⇒ Object (also: #to_s)
- #reference_number_for_label ⇒ Object
- #rejoin_serial_numbers ⇒ Object
- #relevant_changes ⇒ Object
- #relink_payments ⇒ Object
- #remap_legacy_shipping_options_if_any ⇒ Object
- #reported_carrier ⇒ Object
- #reported_master_tracking_number ⇒ Object
- #requires_manifest_completion? ⇒ Boolean
-
#reset_early_label_flag_on_order ⇒ Object
Reset purchase_label_early flag on the order so next ship-label goes through normal flow.
- #reset_ship_labeled_at ⇒ Object
- #reset_shipping_cost ⇒ Object
- #reset_ships_economy_if_unselected ⇒ Object
- #resource ⇒ Object
- #resource_or_rma_for_delivery ⇒ Object
- #resource_present ⇒ Object
- #resource_shipping_method ⇒ Object
-
#retrieve_friendly_shipping_method(show_customer_pays_info = false, for_www = false, sc = nil, with_delivery_commitment = false, for_edi = false) ⇒ Object
rescue "n/a".
-
#retrieve_shipping_costs(rate_ship_date: nil) ⇒ Object
Retrieve shipping costs from carriers.
- #retrieve_shipping_description_for_line_item(shipping_line) ⇒ Object
- #retrieve_shipping_description_for_shipping_cost(sc = nil) ⇒ Object
- #revert_to_override_economy_shipping_method(autosave = true) ⇒ Object
- #save_purchase_order_if_needed(po) ⇒ Object
- #schedule_pickup_if_necessary ⇒ Object
- #schedule_request_estimated_packaging ⇒ Object
- #send_address_type_issue_notification ⇒ Object
- #send_canada_post_manual_void_email(tracking_numbers) ⇒ Object
- #send_commercial_invoice_to_carrier ⇒ Object
- #send_delivery_pre_pack_cancelled_notification(cancelled_by: nil) ⇒ Object
- #send_delivery_pre_packed_notification ⇒ Object
- #send_dropship_delivery_notification ⇒ Object
- #send_purolator_manual_void_email(tracking_numbers) ⇒ Object
- #serial_numbers_file_name ⇒ Object
- #serial_numbers_to_print ⇒ Object
- #set_cogs ⇒ Object
- #set_master_tracking_and_actual_shipping_cost_if_needed ⇒ Object
- #set_override_shipping(autosave = true) ⇒ Object
- #set_packaged_items_md5_hash(options = {}) ⇒ Object
- #set_proper_shipping_cost ⇒ Object
- #set_ship_labeled_at ⇒ Object
- #set_shipped_date ⇒ Object
- #ship_ci_pdf ⇒ Object
- #ship_from_attributes ⇒ Object
- #ship_labeled_via_heatwave? ⇒ Boolean
- #ship_labeled_via_heatwave_or_manual_and_ship_insuring? ⇒ Boolean
- #ship_natively_key ⇒ Object
- #ship_to_attributes ⇒ Object
- #shipment_contents_editable?(current_user = nil) ⇒ Boolean
- #shipments_for_packing ⇒ Object
-
#shipments_to_packages_hash(use_shipments = nil) ⇒ Object
Bridge method from Shipments to package hash model used by WyShipping.
- #shipments_voidable? ⇒ Boolean
- #shipping? ⇒ Boolean
- #shipping_line_item ⇒ Object
- #shipping_method_friendly ⇒ Object
- #shipping_methods_for_select(verbose = false, skip_override = false) ⇒ Object
- #shipping_option_matches?(so_name) ⇒ Boolean
-
#ships_economy? ⇒ Boolean
(also: #ships_economy)
don't know why, but need to do it this way, can't use delegate.
- #ships_economy_ltl? ⇒ Boolean
- #ships_economy_package? ⇒ Boolean
- #ships_ltl_freight? ⇒ Boolean
- #should_have_electronic_commercial_invoice? ⇒ Boolean
- #should_print_heating_element_labels? ⇒ Boolean
- #should_send_commercial_invoice_to_carrier? ⇒ Boolean
- #should_ship_ltl_freight? ⇒ Boolean
- #show_packaging_on_pick_slip? ⇒ Boolean
- #simple_shipping_description_for_shipping_cost(sc = nil) ⇒ Object
-
#skip_destination_address_validation? ⇒ Boolean
Skip destination_address validation for instant quotes and shopping carts Carts don't have a shipping address until checkout.
- #sorted_ground_shipping_costs(skip_override = false) ⇒ Object
- #sorted_shipping_costs(skip_override: false, uniq_by_shipping_option_id: false, filter_by_ltl_freight: nil) ⇒ Object
- #sorted_shipping_costs_www_hash(sort_by_price: true) ⇒ Object
- #split_serial_numbers ⇒ Object
- #state_list ⇒ Object
- #supported_shipping_carrier? ⇒ Boolean
- #tracking_link ⇒ Object
- #uncommit_catalog_items ⇒ Object
- #uncommit_reserved_serial_numbers ⇒ Object
- #unlink_serial_numbers_to_line_items ⇒ Object
-
#update_line_items_qty_shipped ⇒ Object
Updates qty_shipped to match quantity for all line items in this delivery.
- #update_serial_numbers_shipped_count ⇒ Object
- #valid_for_generating_return_labels? ⇒ Boolean
- #valid_for_generating_ship_labels? ⇒ Boolean
- #valid_for_voiding_ship_labels? ⇒ Boolean
- #validate_all_contents_allocated ⇒ Object
- #versions_for_audit_trail(_params = {}) ⇒ Object
-
#void_early_label_on_order ⇒ Boolean
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).
-
#void_early_label_on_order_if_exists ⇒ Object
Void the early-purchased label on the order if one exists (regardless of whether it was transferred to a shipment).
-
#void_marketplace_labels ⇒ Object
Void marketplace labels (Walmart SWW, Amazon, etc.) for all shipments with labels.
- #void_shipments ⇒ Object
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
Methods included from Models::Md5Hashable
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::EventPublishable
Instance Attribute Details
#do_not_validate_line_items ⇒ Object
Returns the value of attribute do_not_validate_line_items.
92 93 94 |
# File 'app/models/delivery.rb', line 92 def do_not_validate_line_items @do_not_validate_line_items end |
#force_shipping_cost_update ⇒ Object
Returns the value of attribute force_shipping_cost_update.
92 93 94 |
# File 'app/models/delivery.rb', line 92 def force_shipping_cost_update @force_shipping_cost_update end |
#override_carrier ⇒ Object
Returns the value of attribute override_carrier.
92 93 94 |
# File 'app/models/delivery.rb', line 92 def override_carrier @override_carrier end |
#override_future_release_date ⇒ Object
Returns the value of attribute override_future_release_date.
92 93 94 |
# File 'app/models/delivery.rb', line 92 def override_future_release_date @override_future_release_date end |
#payment_ids ⇒ Object
Returns the value of attribute payment_ids.
92 93 94 |
# File 'app/models/delivery.rb', line 92 def payment_ids @payment_ids end |
Class Method Details
.active ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are active. Active Record Scope
284 |
# File 'app/models/delivery.rb', line 284 scope :active, -> { where.not(state: 'cancelled') } |
.all_at_warehouse ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are all at warehouse. Active Record Scope
273 |
# File 'app/models/delivery.rb', line 273 scope :all_at_warehouse, -> { where(state: WAREHOUSE_STATES) } |
.auto_ship_confirm(logger: nil) ⇒ Object
3423 3424 3425 3426 3427 3428 3429 3430 3431 |
# File 'app/models/delivery.rb', line 3423 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_fulfillment ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are awaiting po fulfillment. Active Record Scope
290 |
# File 'app/models/delivery.rb', line 290 scope :awaiting_po_fulfillment, -> { where(state: 'awaiting_po_fulfillment') } |
.by_order_store_id ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are by order store id. Active Record Scope
270 |
# File 'app/models/delivery.rb', line 270 scope :by_order_store_id, ->(store_id) { where(SUBQUERY_ORDER_STORE_ID, store_id:) } |
.by_quote_store_id ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are by quote store id. Active Record Scope
271 |
# File 'app/models/delivery.rb', line 271 scope :by_quote_store_id, ->(store_id) { where(SUBQUERY_QUOTE_STORE_ID, store_id:) } |
.by_store_id ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are by store id. Active Record Scope
269 |
# File 'app/models/delivery.rb', line 269 scope :by_store_id, ->(store_id) { by_order_store_id(store_id).or(by_quote_store_id(store_id)) } |
.cancelable ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are cancelable. Active Record Scope
285 |
# File 'app/models/delivery.rb', line 285 scope :cancelable, -> { where(state: CANCELABLE_STATES) } |
.dropship ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are dropship. Active Record Scope
289 |
# File 'app/models/delivery.rb', line 289 scope :dropship, -> { where(state: %w[awaiting_po_fulfillment processing_po_fulfillment]) } |
.fedex_express ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are fedex express. Active Record Scope
299 |
# File 'app/models/delivery.rb', line 299 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_ground ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are fedex ground. Active Record Scope
298 |
# File 'app/models/delivery.rb', line 298 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_release ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are for future release. Active Record Scope
295 |
# File 'app/models/delivery.rb', line 295 scope :for_future_release, -> { where(state: 'future_release') } |
.generate_super_pick_slip_pdf(deliveries, split_kits = false) ⇒ Object
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 |
# File 'app/models/delivery.rb', line 1098 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
3413 3414 3415 3416 3417 3418 3419 3420 3421 |
# File 'app/models/delivery.rb', line 3413 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 |
.invoiced ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are invoiced. Active Record Scope
282 |
# File 'app/models/delivery.rb', line 282 scope :invoiced, -> { where(state: 'invoiced') } |
.limit_to_fba ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are limit to fba. Active Record Scope
294 |
# File 'app/models/delivery.rb', line 294 scope :limit_to_fba, ->(fba_only) { fba_only ? joins(order: { customer: :billing_address }).where('parties.id = ? OR addresses.party_id = ?', CustomerConstants::AMAZON_COM_ID, CustomerConstants::AMAZON_COM_ID) : where('1=1') } |
.non_pickups ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are non pickups. Active Record Scope
293 |
# File 'app/models/delivery.rb', line 293 scope :non_pickups, -> { where.not(destination_address_id: WAREHOUSE_ADDRESS_IDS) } |
.non_quoting ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are non quoting. Active Record Scope
280 |
# File 'app/models/delivery.rb', line 280 scope :non_quoting, -> { where.not(state: 'quoting') } |
.not_cancelable ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are not cancelable. Active Record Scope
286 |
# File 'app/models/delivery.rb', line 286 scope :not_cancelable, -> { where.not(state: CANCELABLE_STATES) } |
.pending_manifest_completion ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are pending manifest completion. Active Record Scope
300 |
# File 'app/models/delivery.rb', line 300 scope :pending_manifest_completion, -> { where(state: 'pending_manifest_completion') } |
.pending_ship_confirm ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are pending ship confirm. Active Record Scope
296 |
# File 'app/models/delivery.rb', line 296 scope :pending_ship_confirm, -> { where(state: 'pending_ship_confirm') } |
.pickups ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are pickups. Active Record Scope
292 |
# File 'app/models/delivery.rb', line 292 scope :pickups, -> { where.not(order_id: nil).where(destination_address_id: WAREHOUSE_ADDRESS_IDS) } |
.processing_po_fulfillment ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are processing po fulfillment. Active Record Scope
291 |
# File 'app/models/delivery.rb', line 291 scope :processing_po_fulfillment, -> { where(state: 'processing_po_fulfillment') } |
.quoting ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are quoting. Active Record Scope
278 |
# File 'app/models/delivery.rb', line 278 scope :quoting, -> { where(state: 'quoting') } |
.quoting_or_pre_pack ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are quoting or pre pack. Active Record Scope
279 |
# File 'app/models/delivery.rb', line 279 scope :quoting_or_pre_pack, -> { where(state: %w[quoting pre_pack]) } |
.release_deliveries_past_release_date ⇒ Object
757 758 759 760 761 762 763 764 765 766 |
# File 'app/models/delivery.rb', line 757 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 if d.at_warehouse? InternalMailer.delivery_automatically_released_notification(d).deliver_later else InternalMailer.delivery_automatic_release_failed_notification(d).deliver_later end end end |
.sales_orders ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are sales orders. Active Record Scope
287 |
# File 'app/models/delivery.rb', line 287 scope :sales_orders, -> { active.joins(:order).merge(Order.sales_orders) } |
.send_manual_release_due_notification ⇒ Object
768 769 770 771 772 |
# File 'app/models/delivery.rb', line 768 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| InternalMailer.manual_release_delivery_due_notification(d).deliver_later end end |
.ship_labeled_before ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are ship labeled before. Active Record Scope
301 |
# File 'app/models/delivery.rb', line 301 scope :ship_labeled_before, ->(time) { where(Delivery[:ship_labeled_at].lteq(time)) } |
.shipped ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are shipped. Active Record Scope
281 |
# File 'app/models/delivery.rb', line 281 scope :shipped, -> { where(state: %w[shipped pending_ship_confirm pending_pickup_confirm]) } |
.shipping ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are shipping. Active Record Scope
288 |
# File 'app/models/delivery.rb', line 288 scope :shipping, -> { where(state: SHIPPING_STATES) } |
.states_for_select ⇒ Object
3409 3410 3411 |
# File 'app/models/delivery.rb', line 3409 def self.states_for_select state_machine.states.sort_by(&:human_name).map { |s| [s.human_name, s.value] } end |
.with_active_parent ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are with active parent. Active Record Scope
274 275 276 277 |
# File 'app/models/delivery.rb', line 274 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_associations ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are with associations. Active Record Scope
272 |
# File 'app/models/delivery.rb', line 272 scope :with_associations, -> { includes(:shipments, :origin_address, :destination_address, { quote: [{ opportunity: [{ customer: [:buying_group, { catalog: :store }] }] }] }, order: [{ customer: [:buying_group, { catalog: :store }] }]) } |
.with_line_items ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are with line items. Active Record Scope
283 |
# File 'app/models/delivery.rb', line 283 scope :with_line_items, -> { includes(line_items: { catalog_item: { store_item: :item } }) } |
.with_shipment_carrier ⇒ ActiveRecord::Relation<Delivery>
A relation of Deliveries that are with shipment carrier. Active Record Scope
297 |
# File 'app/models/delivery.rb', line 297 scope :with_shipment_carrier, ->(carrier) { joins(:shipments).where(shipments: { carrier: }) } |
Instance Method Details
#activities ⇒ ActiveRecord::Relation<Activity>
121 |
# File 'app/models/delivery.rb', line 121 has_many :activities, as: :resource, dependent: :nullify |
#actual_shipping_cost_exceeds_threshold? ⇒ Boolean
3515 3516 3517 3518 3519 3520 |
# File 'app/models/delivery.rb', line 3515 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_show ⇒ Object
3503 3504 3505 3506 3507 |
# File 'app/models/delivery.rb', line 3503 def actual_shipping_cost_to_show actual_shipping_cost_to_use = 0.0 actual_shipping_cost_to_use = self.actual_shipping_cost.to_f if chosen_shipping_method&.shipping_account_number.blank? actual_shipping_cost_to_use.round(2) end |
#actual_shipping_cost_within_threshold? ⇒ Boolean
3509 3510 3511 3512 3513 |
# File 'app/models/delivery.rb', line 3509 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_weight ⇒ Object Also known as: actual_shipment_weight
3522 3523 3524 3525 3526 3527 3528 3529 3530 |
# File 'app/models/delivery.rb', line 3522 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
3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 |
# File 'app/models/delivery.rb', line 3533 def actual_weight_discrepancy_exceeds_threshold? wt_diff_exceeds_percent_thresh = false wt_diff_exceeds_percent_thresh = true if (actual_weight - ship_weight).abs > (Delivery::SHIP_LABEL_HOLD_WEIGHT_PERCENT_THRESHOLD / 100).round(2) * ship_weight wt_diff_exceeds_lbs_threshold = false wt_diff_exceeds_lbs_threshold = true if (actual_weight - ship_weight).abs > Delivery::SHIP_LABEL_HOLD_WEIGHT_THRESHOLD exceeds = false exceeds = true if 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}, estimated ship_weight: #{ship_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) ⇒ Object
2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 |
# File 'app/models/delivery.rb', line 2635 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) ⇒ Object
2626 2627 2628 2629 2630 2631 2632 2633 |
# File 'app/models/delivery.rb', line 2626 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_cost ⇒ Object
3220 3221 3222 3223 3224 3225 3226 |
# File 'app/models/delivery.rb', line 3220 def adjusted_actual_shipping_cost if chosen_shipping_method&.shipping_account_number.present? BigDecimal(0) else actual_shipping_cost end end |
#all_activities ⇒ Object
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 |
# File 'app/models/delivery.rb', line 690 def all_activities # Start with the initial condition for Delivery query = Activity.where( Activity.arel_table[:resource_type].eq('Delivery') .and(Activity.arel_table[:resource_id].eq(id)) ) # Add the Order condition if order_id is present if order_id.present? order_condition = Activity.where( Activity.arel_table[:resource_type].eq('Order') .and(Activity.arel_table[:resource_id].eq(order_id)) ) query = query.or(order_condition) end # Add the Quote condition if quote_id is present if quote_id.present? quote_condition = Activity.where( Activity.arel_table[:resource_type].eq('Quote') .and(Activity.arel_table[:resource_id].eq(quote_id)) ) query = query.or(quote_condition) end query end |
#all_dropship_items_fulfilled? ⇒ Boolean
2560 2561 2562 |
# File 'app/models/delivery.rb', line 2560 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_pdf ⇒ Object
3086 3087 3088 |
# File 'app/models/delivery.rb', line 3086 def all_intl_forms_pdf uploads.order(:id).reverse_order.find_by(category: 'all_intl_forms_pdf') end |
#all_labels_pdf ⇒ Object
3066 3067 3068 |
# File 'app/models/delivery.rb', line 3066 def all_labels_pdf uploads.order(:id).reverse_order.find_by(category: 'all_labels_pdf') end |
#all_lines_allocated_to_shipments? ⇒ Boolean
798 799 800 |
# File 'app/models/delivery.rb', line 798 def all_lines_allocated_to_shipments? line_allocation_status_hash.values.all?(&:zero?) end |
#all_lines_allocated_to_shipments_and_shipments_have_weight? ⇒ Boolean
802 803 804 805 806 807 808 809 810 811 812 813 814 |
# File 'app/models/delivery.rb', line 802 def all_lines_allocated_to_shipments_and_shipments_have_weight? res = true unless all_lines_allocated_to_shipments? errors.add(:base, 'All items must be allocated to shipments.') res = false end # CB: Disabled for now, causing a lot of frictions with warehouse. Ramie when back will revisit. # unless shipments.all?{|s| s.weight > s.compute_tare_weight && s.entered_and_computed_weights_are_close?} # errors.add(:base, "All shipments must have a weight close the computed item weight plus the tare/packaging weight. Shipments weights: #{shipments.map{|s| s.weight.to_f.to_s + 'lbs'}.join(', ')}, computed weights: #{shipments.map{|s| (s.compute_tare_weight.to_f + s.compute_shipment_weight.to_f).to_s + 'lbs'}.join(', ')}") # res = false # end res end |
#all_shipments_weights_match_expected ⇒ Object
3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 |
# File 'app/models/delivery.rb', line 3612 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 |
#apply_cheapest_economy_shipping_method ⇒ Object
1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 |
# File 'app/models/delivery.rb', line 1696 def apply_cheapest_economy_shipping_method return true unless selected_shipping_cost&.is_override? # only applies to override ships economy retrieve_shipping_costs # get new shipping costs post-pack self.selected_shipping_cost = sorted_shipping_costs_www_hash[:ground]&.first # choose cheapest ground save if order&.customer_qualifies_for_free_online_shipping? # if order had free shipping online coupon, remove it now free_online_shipping_discount = order.discounts.free_online_shipping.first Coupon::DeleteDiscount.new.perform(free_online_shipping_discount, { skip_lock_check: true }) end order&.reload&.reset_discount(reset_item_pricing: false) # reset the discount apply_shipping_match_for_economy_shipping true end |
#apply_selected_shipping_cost!(shipping_cost_entry, previous_selected_id: nil, persist: false) ⇒ Object
Centralized application of the selected shipping cost to delivery and its shipping line
Ensures consistency whether selection is auto-computed or explicitly chosen elsewhere
2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 |
# File 'app/models/delivery.rb', line 2448 def apply_selected_shipping_cost!(shipping_cost_entry, previous_selected_id: nil, persist: false) unless shipping_cost_entry.shipping_option.item raise "Cannot create shipping line item as item is missing from shipping_option ID: #{begin shipping_cost_entry.shipping_option_id rescue StandardError nil end}" end shipping_line = line_items.detect(&:is_shipping?) shipping_line ||= line_items.build(resource:, tax_class: 'shp') shipping_line.quantity = is_rma_return? ? -1 : 1 shipping_line.item = shipping_cost_entry.shipping_option.item shipping_line.price = shipping_cost_entry.calculated_cost # Clear old line_discounts when shipping changes - reset_discount will recalculate them if previous_selected_id && previous_selected_id != shipping_cost_entry.id if persist shipping_line.line_discounts.destroy_all else shipping_line.line_discounts.each(&:mark_for_destruction) end end shipping_line.discounted_price = shipping_cost_entry.calculated_cost shipping_line.shipping_cost = shipping_cost_entry self.selected_shipping_cost_id = shipping_cost_entry.id shipping_line.description = retrieve_shipping_description_for_shipping_cost(shipping_cost_entry) self.shipping_cost = shipping_cost_entry.calculated_cost carrier = shipping_cost_entry&.shipping_option&.carrier shipments.each do |s| Rails.logger.debug { "apply_selected_shipping_cost!, before: carrier: #{carrier}, s.carrier: #{s.carrier}" } s.update(carrier:) Rails.logger.debug { "apply_selected_shipping_cost!, after: carrier: #{carrier}, s.carrier: #{s.carrier}" } end self.do_not_recalculate = true if persist # Persist delivery columns without invoking callbacks update_columns( shipping_option_id: shipping_cost_entry.shipping_option_id, shipping_cost: shipping_cost_entry.calculated_cost, selected_shipping_cost_id: shipping_cost_entry.id, updated_at: Time.current ) # Persist shipping line shipping_line.save! # Remove any extra shipping lines now that the current one is saved extra_shipping_lines = line_items.select(&:is_shipping?) - [shipping_line] extra_shipping_lines.each(&:destroy) # Ensure itemizable knows about the shipping line resource.sync_shipping_line if resource.respond_to?(:sync_shipping_line) else extra_shipping_lines = line_items.select(&:is_shipping?) - [shipping_line] extra_shipping_lines.each(&:destroy) end resource.do_not_set_totals = nil if resource.present? if resource && !resource.editing_locked? resource.force_total_reset = true end Rails.logger.debug { "apply_selected_shipping_cost!, after: shipping_line: #{shipping_line.inspect}" } end |
#apply_shipping_match_for_economy_shipping ⇒ Object
1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 |
# File 'app/models/delivery.rb', line 1711 def apply_shipping_match_for_economy_shipping order.reload economy_shipping_match_crm_coupon = Coupon.find_by_code(Coupon::ECONOMY_SHIPPING_MATCH_CRM_COUPON_CODE) return unless economy_shipping_match_crm_coupon.present? && order.line_items.shipping_only.present? # only proceed if we have the shipping line, coupon if order.discounts.with_economy_shipping_match_crm.present? # if order had economy_shipping_match_crm_coupon, remove it now economy_shipping_match_crm_discount = order.discounts.with_economy_shipping_match_crm.first Coupon::DeleteDiscount.new.perform(economy_shipping_match_crm_discount, { skip_lock_check: true }) order.reload.reset_discount(reset_item_pricing: false) # reset the discount end amount = (order.shipping_cost_at_time_of_checkout || order.shipping_cost) - order.shipping_cost # order.shipping_cost_at_time_of_checkout should NEVER be nil, but hey it happens, very rarely, for no explicable reason, the fix is to set it to order.shipping_cost return unless amount.abs > 0.0 # only proceed if we have an order balance discount = Discount.new(itemizable: order, coupon_id: economy_shipping_match_crm_coupon.id, effective_date: Date.current) shipping_li = order.line_items.shipping_only.first discount.line_discounts.build(coupon_id: economy_shipping_match_crm_coupon.id, amount:, line_item_id: shipping_li.id) if discount.line_discounts.any? discount.amount = discount.user_amount = discount.line_discounts.to_a.sum(&:amount) discount.save! end order.reload.calculate_tax_for_all_lines end |
#apply_warehouse_fee? ⇒ Boolean
2115 2116 2117 2118 |
# File 'app/models/delivery.rb', line 2115 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_shipment ⇒ Object
2725 2726 2727 2728 2729 2730 2731 2732 |
# File 'app/models/delivery.rb', line 2725 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).
790 791 792 |
# File 'app/models/delivery.rb', line 790 def Packing.where(delivery_id: id, origin: %i[from_delivery from_manual_entry]).exists? end |
#billing_entity ⇒ Object
Alias for Resource#billing_entity
155 |
# File 'app/models/delivery.rb', line 155 delegate :billing_entity, to: :resource |
#bol_pdf ⇒ Object
3070 3071 3072 |
# File 'app/models/delivery.rb', line 3070 def bol_pdf uploads.ship_bol_pdfs.first end |
#build_new_purchase_order_item(item, li, quantity, unit_cost) ⇒ Object
2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 |
# File 'app/models/delivery.rb', line 2652 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) ⇒ Object
2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 |
# File 'app/models/delivery.rb', line 2605 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_cogs ⇒ Object
725 726 727 |
# File 'app/models/delivery.rb', line 725 def calculate_all_cogs BigDecimal(line_items.non_shipping.sum('unit_cogs * quantity')) end |
#calculate_declared_value ⇒ Object
3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 |
# File 'app/models/delivery.rb', line 3279 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_total ⇒ Object
3275 3276 3277 |
# File 'app/models/delivery.rb', line 3275 def calculate_grand_total (actual_shipping_cost || 0.0) + subtotal end |
#calculate_shipping_options(options = {}) ⇒ Object
1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 |
# File 'app/models/delivery.rb', line 1606 def ( = {}) # TBD REFACTOR NO ORDER DEPENDENCY FOR RETURN DELIVERY [:resource_shipping_method] = (resource&.try(:edi_shipping_option_name) || resource_shipping_method) if resource&.try(:edi_shipping_option_name) [:force_shipping_carrier] = (ShippingOption.active.where(name: resource.try(:edi_shipping_option_name))&.last&.carrier || ShippingOption.where(name: resource.try(:edi_shipping_option_name))&.last&.carrier) end [:origin_address] = origin_address [:origin_country_iso3] = origin_address.country.iso3 if destination_address.present? [:city] = destination_address.city [:state] = destination_address.state_code [:country_iso] = destination_address.country.iso [:postal_code] = destination_address.zip_compact.to_s.upcase.strip.squeeze(' ') [:is_residential] = destination_address.is_residential [:destination_address] = destination_address elsif resource.installation_postal_code.present? [:state] = resource.installation_state_code [:country_iso] = resource.installation_country_iso [:postal_code] = resource.installation_postal_code end # As far as i can tell this is useless when we use a scope to load them # options[:line_items] = line_items.select { |li| !li.destroyed? && !li.marked_for_destruction? && !li.is_shipping? && !li.is_service? } # This skips deleted, shipping and service line items [:store] = customer.store [:saturday_delivery] = saturday_delivery [:signature_confirmation] = signature_confirmation.to_b [:myprojects_legacy] = false if [:country_iso] == 'CA' # kludge [:ltl_freight] = is_default_ltl_freight? || ltl_freight.to_b || ltl_freight_guaranteed.to_b # puts "!!!!!!!!options[:ltl_freight]: #{options[:ltl_freight]}, self.is_default_ltl_freight?: #{self.is_default_ltl_freight?}, self.ltl_freight: #{self.ltl_freight}, self.ltl_freight_guaranteed: #{self.ltl_freight_guaranteed}" else [:ltl_freight] = ltl_freight.to_b [:ltl_freight_guaranteed] = ltl_freight_guaranteed.to_b end [:insured_value] = calculate_declared_value use_shipments = Shipping::CreateSuggestedShipment.new.process(self) .merge!(shipments_to_packages_hash(use_shipments.select { |s| s.parent_shipment_id.nil? })) cod_amount = 0.0 cod_amount = resource&.total if cod_collection_type && resource [:cod_collection_type] = cod_collection_type [:cod_amount] = cod_amount # Limit carriers for RMA returns to carriers assigned for RMAs in the country if is_rma_return? rma_country = rma_for_return&.customer&.store&.country&.iso if rma_country.present? rma_sos = ShippingOption.active.for_rmas .where(country: rma_country) .where.not(carrier: nil) [:allowed_carriers] = rma_sos.distinct.pluck(:carrier) [:limit_service_codes] = rma_sos.where.not(service_code: nil).pluck(:service_code) end end # options[:media_mail] = true if self.line_items.goods.all?{|li| li.is_publication?} # apparently only for educational materials, see:https://about.usps.com/notices/not121/not121_tech.htm # puts "calculate_shipping_options: #{options.inspect}" if resource&.try(:is_www_ship_by_zip) || (resource&.try(:in_shipping_estimate?) && resource&.try(:shipping_address_id).nil?) [:is_www_ship_by_zip] = true elsif resource&.try(:is_www) [:www] = true end end |
#can_be_deleted? ⇒ Boolean
666 667 668 |
# File 'app/models/delivery.rb', line 666 def can_be_deleted? quoting? || pre_pack? || picking? || at_warehouse? || (order && order.order_type == Order::CREDIT_ORDER) end |
#can_print_carton_labels? ⇒ Boolean
674 675 676 |
# File 'app/models/delivery.rb', line 674 def can_print_carton_labels? shipments.packed_or_measured.present? end |
#can_update_tracking_info? ⇒ Boolean
670 671 672 |
# File 'app/models/delivery.rb', line 670 def can_update_tracking_info? shipments.completed.present? end |
#can_void_rma_delivery? ⇒ Boolean
749 750 751 |
# File 'app/models/delivery.rb', line 749 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
2058 2059 2060 2061 2062 2063 2064 2065 |
# File 'app/models/delivery.rb', line 2058 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 |
#cancel ⇒ Object
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 |
# File 'app/models/delivery.rb', line 847 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_packaging ⇒ Object
3757 3758 3759 |
# File 'app/models/delivery.rb', line 3757 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
2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 |
# File 'app/models/delivery.rb', line 2525 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).
2755 2756 2757 |
# File 'app/models/delivery.rb', line 2755 def cannot_ship_empty_delivery? !has_shippable_content? end |
#carrier_customs_email ⇒ Object
2716 2717 2718 2719 2720 2721 2722 2723 |
# File 'app/models/delivery.rb', line 2716 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_icon ⇒ Object
3186 3187 3188 |
# File 'app/models/delivery.rb', line 3186 def carrier_icon Shipment.carrier_icon(carrier) end |
#carrier_options_for_select ⇒ Object
794 795 796 |
# File 'app/models/delivery.rb', line 794 def Shipment.(self) end |
#catalog ⇒ Object
Alias for Customer#catalog
156 |
# File 'app/models/delivery.rb', line 156 delegate :catalog, :is_amazon_seller_central?, to: :customer |
#chosen_shipping_method ⇒ Object
1884 1885 1886 1887 1888 |
# File 'app/models/delivery.rb', line 1884 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_cost ⇒ Object
1865 1866 1867 1868 1869 |
# File 'app/models/delivery.rb', line 1865 def chosen_shipping_method_carrier_cost c = chosen_shipping_method&.cost.to_f rd = chosen_shipping_method&.rate_data (c == 0.0 && rd.present? ? rd['actual_cost'].to_f : c) end |
#commit_catalog_items ⇒ Object
3248 3249 3250 |
# File 'app/models/delivery.rb', line 3248 def commit_catalog_items Item::InventoryCommitter.crm_commit(line_items) end |
#commit_reserved_serial_numbers ⇒ Object
3252 3253 3254 |
# File 'app/models/delivery.rb', line 3252 def commit_reserved_serial_numbers line_items.each(&:commit_reserved_serial_numbers) end |
#complete_picked ⇒ Object
3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 |
# File 'app/models/delivery.rb', line 3729 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 if order&.pre_pack? # order pre_pack flow has its own notifications if order&.is_wasn4_or_wat0f? order.request_carrier_assignment 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 set_packaged_items_md5_hash(origin: :from_manual_entry) # for all pre-packs, add to md5 Packing db 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
1005 1006 1007 |
# File 'app/models/delivery.rb', line 1005 def completed_regular_delivery? (pending_pickup_confirm? || shipped? || invoiced?) && !is_service_only? end |
#copy_shipments_if_drop_ship_po ⇒ Object
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.
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 |
# File 'app/models/delivery.rb', line 3464 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 |
#country ⇒ Object
3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 |
# File 'app/models/delivery.rb', line 3291 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_invoice ⇒ Object
3397 3398 3399 3400 3401 3402 3403 |
# File 'app/models/delivery.rb', line 3397 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') ⇒ Object
3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 |
# File 'app/models/delivery.rb', line 3665 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_entries ⇒ Object
3392 3393 3394 3395 |
# File 'app/models/delivery.rb', line 3392 def create_st_ledger_entries LedgerTransaction.process_intracompany_st_delivery(self) ItemLedgerEntry.process_intracompany_st_delivery(self) end |
#currency ⇒ Object
3306 3307 3308 |
# File 'app/models/delivery.rb', line 3306 def currency order&.currency || resource&.currency || rma_for_return&.customer&.catalog&.currency end |
#currency_symbol ⇒ Object
3310 3311 3312 |
# File 'app/models/delivery.rb', line 3310 def currency_symbol Money::Currency.new(currency).symbol end |
#custom_pack_list ⇒ Object
1073 1074 1075 1076 1077 1078 |
# File 'app/models/delivery.rb', line 1073 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 |
#customer ⇒ Object
Alias for Resource_or_rma_for_delivery#customer
154 |
# File 'app/models/delivery.rb', line 154 delegate :customer, :primary_party, to: :resource_or_rma_for_delivery |
#default_container_type ⇒ Object
3723 3724 3725 3726 3727 |
# File 'app/models/delivery.rb', line 3723 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_reservations ⇒ Object
3574 3575 3576 |
# File 'app/models/delivery.rb', line 3574 def delete_serial_number_reservations line_items.each { |li| li.reserved_serial_numbers.destroy_all } end |
#delta_weight_factor_remaining_to_allocate ⇒ Object
820 821 822 |
# File 'app/models/delivery.rb', line 820 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_address ⇒ Address
Validations (unless => #skip_destination_address_validation? ):
103 |
# File 'app/models/delivery.rb', line 103 belongs_to :destination_address, class_name: 'Address', validate: true, optional: true |
#discounts ⇒ ActiveRecord::Relation<Discount>
114 |
# File 'app/models/delivery.rb', line 114 has_many :discounts, through: :line_discounts |
#display_carrier_name ⇒ Object
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.
1984 1985 1986 |
# File 'app/models/delivery.rb', line 1984 def display_carrier_name chosen_shipping_method&.rate_data&.[]('carrier_name') || reported_carrier end |
#do_not_ship_insure_via_carrier? ⇒ Boolean
1875 1876 1877 1878 1879 1880 1881 1882 |
# File 'app/models/delivery.rb', line 1875 def do_not_ship_insure_via_carrier? res = false res = true if Shipping::ShippingInsurance.new.(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 end |
#do_not_validate_line_items? ⇒ Boolean
2734 2735 2736 |
# File 'app/models/delivery.rb', line 2734 def do_not_validate_line_items? do_not_validate_line_items end |
#does_not_require_shipping_labeling? ⇒ Boolean
3796 3797 3798 3799 |
# File 'app/models/delivery.rb', line 3796 def does_not_require_shipping_labeling? order&.is_store_transfer? && customer&.id&.in?(CustomerConstants::TRANSFER_TO_WY_CUSTOMER_IDS) end |
#drop_ship_purchase_orders ⇒ ActiveRecord::Relation<PurchaseOrder>
117 |
# File 'app/models/delivery.rb', line 117 has_many :drop_ship_purchase_orders, class_name: 'PurchaseOrder', foreign_key: 'drop_ship_delivery_id' |
#electronic_ship_ci_pdf ⇒ Object
3078 3079 3080 |
# File 'app/models/delivery.rb', line 3078 def electronic_ship_ci_pdf uploads.order(:id).reverse_order.find_by(category: 'electronic_ship_ci_pdf') end |
#estimated_delivery_date ⇒ Object
Trying to 'guess' the delivery date
659 660 661 662 663 664 |
# File 'app/models/delivery.rb', line 659 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 |
#european_shipment? ⇒ Boolean
3801 3802 3803 |
# File 'app/models/delivery.rb', line 3801 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) ⇒ Object
2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 |
# File 'app/models/delivery.rb', line 2779 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) ⇒ Object
1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 |
# File 'app/models/delivery.rb', line 1852 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 |
#freightquote_carrier? ⇒ Boolean
1977 1978 1979 |
# File 'app/models/delivery.rb', line 1977 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) ⇒ Object
1918 1919 1920 1921 1922 1923 1924 |
# File 'app/models/delivery.rb', line 1918 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_edi ⇒ Object
1965 1966 1967 |
# File 'app/models/delivery.rb', line 1965 def friendly_shipping_method_for_edi retrieve_friendly_shipping_method(false, false, nil, false, true) end |
#generate_all_international_forms_pdf ⇒ Object
2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 |
# File 'app/models/delivery.rb', line 2843 def generate_all_international_forms_pdf res = true # puts "self.generate_all_international_forms_pdf, delivery #{self.id}: #{self.inspect}" all_forms = [ship_ci_pdf, bol_pdf].compact # Include USMCA/FTA certificates attached to items on this delivery begin item_ids = line_items.goods.pluck(:item_id) if item_ids.present? usmca_cert_uploads = Upload.where(category: 'usmca_certificate') .joins(:items) .where(items: { id: item_ids }) .to_a steel_decl = Upload.where(category: 'declaration_of_steel_aluminimum_copper').order(id: :desc).first usmca_cert_uploads << steel_decl if steel_decl all_forms.concat(usmca_cert_uploads) if usmca_cert_uploads.present? end rescue StandardError => e Rails.logger.warn("generate_all_international_forms_pdf: USMCA cert include failed: #{e}") end # Add Commercial Invoice if all_forms.present? file_name = "#{name(false, true)}_all_intl_forms_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}.pdf".downcase all_forms_path = Rails.application.config.x.temp_storage_path.join(file_name) # puts "self.generate_all_international_forms_pdf: all_forms_path: #{all_forms_path}, all_forms: #{all_forms.inspect}" PdfTools.combine(all_forms, output_file_path: all_forms_path) upload = Upload.uploadify(all_forms_path, 'all_intl_forms_pdf') raise 'Combo international forms was not generated' unless upload res = uploads << upload end res end |
#generate_all_labels_pdf ⇒ Object
2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 |
# File 'app/models/delivery.rb', line 2805 def generate_all_labels_pdf all_shipments = shipments.completed.order(:id).includes(:uploads) # Batching since the tmp folder will only reliably hold ~15 tmp files before GC, so use batches of 10 batch_size = 10 (all_shipments.size.to_f / batch_size.to_f).ceil.times do |i| all_shipments_batch = all_shipments[(i * batch_size)..((batch_size * (i + 1)) - 1)] all_labels = [] all_shipments_batch.each do |s| label = s.uploads.detect { |u| u.category == 'ship_label_pdf' } next unless label all_labels << label end rmas = [] rmas << order.rma if order&.is_rma_replacement? rmas << precreated_rma if precreated_rma.present? rmas.each do |rma| rma.credit_orders.each do |co| co.shipments.label_complete.order(:id).includes(:uploads).each do |s| label = s.uploads.detect { |u| u.category == 'ship_label_pdf' } all_labels << label if label end end end serial_numbers_pdf = generate_serial_numbers_pdf all_labels << serial_numbers_pdf unless serial_numbers_pdf.nil? all_labels.concat(order.uploads.in_category('master_carton_label')) if order # include any master carton labels file_name = "batch_#{i}_#{name(false, true)}_all_labels_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}.pdf".downcase all_labels_path = Rails.application.config.x.temp_storage_path.join(file_name) Rails.logger.debug("generate_all_labels_pdf", path: all_labels_path, label_count: all_labels&.size) PdfTools.combine(all_labels, output_file_path: all_labels_path) upload = Upload.uploadify(all_labels_path, 'all_labels_pdf') raise 'Combo label was not generated' unless upload uploads << upload end end |
#generate_asynch_labels ⇒ Object
ScoutAPM Disabled instrument_method :generate_labels
3059 3060 3061 3062 3063 3064 |
# File 'app/models/delivery.rb', line 3059 def generate_asynch_labels generate_bol_pdf generate_ci_pdf generate_all_labels_pdf generate_all_international_forms_pdf end |
#generate_barcode ⇒ Object
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 |
# File 'app/models/delivery.rb', line 1129 def require 'barby' require 'barby/barcode/code_128' require 'barby/outputter/png_outputter' = Barby::Code128B.new(order.reference_number.to_s) path = File.join(Rails.application.config.x.temp_storage_path.to_s, "#{name(false, true)}_generated_barcode_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}.png") File.open(path, 'wb') do |file| file.write .to_png(height: 70) file.flush file.fsync end path end |
#generate_bol_pdf(ship_bol_tmp_path = nil) ⇒ Object
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 |
# File 'app/models/delivery.rb', line 1143 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) ⇒ Object
1178 1179 1180 1181 1182 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 1178 def generate_ci_pdf(ci_tmp_path = nil) # if we have carrier generated CI, use that # UPSFreight generates a CI in triplicate ie x 3 if ci_tmp_path # generate the pdf from the file path, note that this means it is a UPS **electronic** Commercial Invoice, not to be printed but stored for future reference. file_name = "order_#{order.reference_number}_delivery_#{index}_#{Time.current.strftime('%m_%d_%Y_%I_%M%p')}_ci.pdf" ci_pdf_path = Upload.temp_location(file_name) FileUtils.cp(ci_tmp_path, ci_pdf_path) # attach the finished CI pdf upload = Upload.uploadify(ci_pdf_path, 'electronic_ship_ci_pdf', nil, file_name) else # otherwise try to generate it using our template from scratch pdf_data = Pdf::Document::CommercialInvoice.new(self).call.pdf combined_pdf = PdfCombinator.new 3.times do combined_pdf << pdf_data end path = Upload.temp_location("#{reference_number}_CI.pdf") File.open(path, 'wb') do |file| file.write combined_pdf.to_pdf file.flush file.fsync end upload = Upload.uploadify(path, 'ship_ci_pdf', nil, "#{reference_number}_CI.pdf") end raise 'CI PDF was not generated' unless upload uploads << upload end |
#generate_combined_pdf(split_kits: false, skip_plans: false) ⇒ Object
this must be here, even if just as a wrapper because the method above calls it!
1064 1065 1066 1067 1068 1069 1070 1071 |
# File 'app/models/delivery.rb', line 1064 def generate_combined_pdf(split_kits: false, skip_plans: false) # this must be here, even if just as a wrapper because the method above calls it! pdf_generator = Pdf::Document::PackingSlip.new(self, { split_kits:, skip_plans: }) pdf_data = pdf_generator.call pdf_data.pages.each { |p| p.orientation :portrait } pdf_data end |
#generate_dropship_po ⇒ Object
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.
2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 |
# File 'app/models/delivery.rb', line 2579 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_labels ⇒ Object
res = uploads << upload
end
res
end
2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 |
# File 'app/models/delivery.rb', line 2896 def generate_labels if pending_ship_labels? && (incurs_oversized_penalty? != true) require 'wy_shipping' shipping_result = WyShipping.ship_delivery(self) Rails.logger.info("#{Time.current}: Order ID #{order&.id}, reference number: #{order&.reference_number}, RMA: #{rma_for_return&.rma_number}, labels generated START LOG $$$$$$$$$$$$$$$$$$$$$$$$$$$$, status_code: #{shipping_result[:status_code]}") if shipping_result[:status_code] == :error Rails.logger.info("#{carrier}, request_xml:") if carrier == 'LegacyUPS' keys_of_interest = %i[confirm_request_xml confirm_response_xml accept_request_xml accept_response_xml] keys_of_interest.each do |key| Rails.logger.info("#{key}:") Rails.logger.info(shipping_result.dig(:shipment, key)) end else # self.carrier == "FedEx" || self.carrier == "UPSFreight" || self.carrier == "Purolator" keys_of_interest = %i[ship_request_xml ship_reply_xml] keys_of_interest.each do |key| Rails.logger.info("#{key}:") Rails.logger.info(shipping_result.dig(:shipment, key)) end end end send_address_type_issue_notification if shipping_result[:flag_address_type_issue] # see if labels generated if shipping_result[:status_code] == :error { status_code: :error, status_message: shipping_result[:status_message] } else # only save if labels successfully generated # first pass get tracking numbers and charges Rails.logger.debug("Shipping result received", delivery_id: id) self.master_tracking_number = shipping_result[:shipment][:shipment_identification_number] self.carrier_bol = shipping_result[:shipment][:carrier_bol] self.pickup_confirmation_number = shipping_result[:shipment][:pickup_confirmation_number] self.shipengine_label_id = shipping_result[:shipment][:shipengine_label_id] self.freight_order_number = shipping_result[:shipment][:freight_order_number] self.actual_shipping_cost = shipping_result[:shipment][:total_charges] Rails.logger.info("master_tracking_number: #{master_tracking_number}, actual_shipping_cost: #{actual_shipping_cost}") # puts "shipping_result[:shipment][:labels].length: #{shipping_result[:shipment][:labels].length}" # puts "shipping_result[:shipment][:labels]: #{shipping_result[:shipment][:labels].inspect}" save # pure hacking to get this to work reload # pure hacking to get this to work shipments.awaiting_label.order('shipments.id ASC').each_with_index do |shipment, j| i = %w[UPSFreight Freightquote RlCarriers Saia YrcFreight].include?(carrier) ? 0 : j Rails.logger.debug { "shipment: #{shipment.inspect}" } next unless label = shipping_result.dig(:shipment, :labels, i) Rails.logger.debug { "shipping_result[:shipment][:labels][i]: #{label&.inspect}" } Rails.logger.debug { "shipping_result[:shipment][:labels][i][:tracking_number]: #{label[:tracking_number]}" } Rails.logger.debug { "shipping_result[:shipment][:labels][i][:shipengine_label_id]: #{label[:shipengine_label_id]}" } shipment.tracking_number = label[:tracking_number] shipment.shipengine_label_id = label[:shipengine_label_id] shipment.label_generated! end Rails.logger.info("#{Time.current}: Order ID #{order&.id}, reference number: #{order&.reference_number}, RMA: #{rma_for_return&.rma_number}, labels generated END LOG $$$$$$$$$$$$$$$$$$$$$$$$$$$$") # save this delivery save # second pass generate the labels from the temporary paths label_exceptions = [] bol_exceptions = [] status_code = :ok shipments.label_complete.order(:id).each_with_index do |shipment, j| # UPS Freight and Freightquote only have a single label, bill of lading etc i = %w[UPSFreight Freightquote RlCarriers Saia YrcFreight].include?(carrier) ? 0 : j old_label_path = (begin shipping_result[:shipment][:labels][i][:image].path rescue StandardError nil end) # skip when label path is nil if old_label_path label_path = old_label_path if shipment.generate_ship_label_pdf(label_path, carrier) # generate letter shipping label pdf for e-mailing rma return if this is an rma return if is_rma_return? if %w[UPS FedEx USPS].include?(carrier) # Shipengine will have the PNG files for each label so use those for image processing rather than the PDF # rotate label 90 left and generate letter ship label label_png_path = shipping_result.dig(:shipment, :labels, i, :png).path new_label_png_path = "#{label_png_path}_rot.png" require 'vips' image = Vips::Image.new_from_file(label_png_path) image = image.rot(:d270) # Rotate 90 degrees counter-clockwise (same as -90) image.write_to_file(new_label_png_path) # Generate letter ship label PDF using the converted image shipment.generate_letter_ship_label_pdf(new_label_png_path, carrier) elsif %w[Canadapost Canpar].include?(carrier) # Canadapost and Canpar will have the PDF files for each label so use those for image processing rather than the PNG # Generate letter ship label PDF using the converted image shipment.generate_letter_ship_label_pdf(nil, carrier) end # FileUtils.rm new_label_png_path end else label_exceptions << (i + 1) end end if carrier == 'SpeedeeDelivery' if shipment.generate_speedee_label_pdf(shipping_result[:shipment][:rate_data]) shipment.generate_letter_ship_label_pdf(nil, carrier) if is_rma_return? else label_exceptions << (i + 1) end end end # generate delivery RMA return instructions and labels here if carrier == 'UPSFreight' || carrier == 'Freightquote' || carrier == 'RlCarriers' || carrier == 'Saia' || carrier == 'YrcFreight' || carrier == 'FedExFreight' ship_bol_pdf_path = (begin shipping_result[:shipment][:labels].first[:bol_image].path rescue StandardError nil end) logger.debug("ship_bol_pdf_path: #{ship_bol_pdf_path}") unless carrier == 'Freightquote' # this will be done asynchronously when we get the load number if generate_bol_pdf(ship_bol_pdf_path) else bol_exceptions << (i + 1) end end end ci_tmp_path = shipping_result.dig(:shipment)&.dig(:ci)&.dig(:image)&.path # Generated by carrier in the case of electronic customs documents if ci_tmp_path || is_international? # check if we need to generate commercial invoice generate_ci_pdf(ci_tmp_path) unless carrier == 'Freightquote' # for Freightquote, we generate this asynchronously after we get the load number end if label_exceptions.empty? && bol_exceptions.empty? && status_code == :ok begin generate_all_labels_pdf unless carrier == 'Freightquote' # this will be done asynchronously when we get the load number = "Labels generated for #{shipments.completed.length} packages. Estimated charge: #{currency_symbol}#{shipping_cost}. Actual charge: #{currency_symbol}#{actual_shipping_cost}. #{}" rescue StandardError => e ErrorReporting.error e = "Labels generated but combined PDF failed: #{e.}. Individual labels are still available." Rails.logger.error("generate_all_labels_pdf failed for delivery #{id}: #{e.class} - #{e.}") end # International forms backgrounded via worker (not needed for label printing) if is_international? && carrier != 'Freightquote' DeliveryPostLabelWorker.perform_async(id) end ship_labeled # implicit save! { status_code:, status_message: } else msg = String.new("Can't generate labels") msg += ", there was an issue with labels #{label_exceptions.map { |l| l + 1 }.join(', ')}" if label_exceptions.any? msg += ", there was an issue with bols #{bol_exceptions.map { |l| l + 1 }.join(', ')}" if bol_exceptions.any? msg += ", status code returned: #{status_code}" unless status_code == :ok msg += '.' { status_code: :error, status_message: msg } end end else error_msg = if incurs_oversized_penalty? "Can't generate labels because the shipping packages exceed oversized limits or include a pallet and would incur big penalties, please review packaging and consider shipping via LTL freight." else "Can't generate labels because the delivery is in the state: #{state.humanize}." end { status_code: :error, status_message: error_msg } end end |
#generate_pick_slip_pdf(split_kits = false) ⇒ Object
1054 1055 1056 1057 1058 1059 1060 1061 1062 |
# File 'app/models/delivery.rb', line 1054 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_pdf ⇒ Object
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 |
# File 'app/models/delivery.rb', line 1085 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) ⇒ Object
1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 |
# File 'app/models/delivery.rb', line 1745 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_use ⇒ Object
3837 3838 3839 |
# File 'app/models/delivery.rb', line 3837 def get_economy_shipping_cost_to_use sorted_shipping_costs_www_hash[:ground]&.first&.cost || get_fallback_cost_to_use end |
#get_existing_po_items ⇒ Object
2601 2602 2603 |
# File 'app/models/delivery.rb', line 2601 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_use ⇒ Object
3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 |
# File 'app/models/delivery.rb', line 3841 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) ⇒ Object
998 999 1000 1001 1002 1003 |
# File 'app/models/delivery.rb', line 998 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..blank?) pdf end |
#group_line_items_by_item(line_items) ⇒ Object
2622 2623 2624 |
# File 'app/models/delivery.rb', line 2622 def group_line_items_by_item(line_items) line_items.group_by(&:item) end |
#group_unprocessed_dropship_line_items_by_supplier ⇒ Object
2596 2597 2598 2599 |
# File 'app/models/delivery.rb', line 2596 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
1174 1175 1176 |
# File 'app/models/delivery.rb', line 1174 def has_custom_products? line_items.joins(:item).where(items: { product_category_id: ProductCategory.custom_product_ids }).exists? end |
#has_custom_shipping_labels? ⇒ Boolean
678 679 680 |
# File 'app/models/delivery.rb', line 678 def has_custom_shipping_labels? order.custom_shipping_labels.present? end |
#has_destination_postal_code ⇒ Object
2519 2520 2521 |
# File 'app/models/delivery.rb', line 2519 def has_destination_postal_code destination_address || resource.installation_postal_code.present? end |
#has_dropship_items? ⇒ Boolean
2564 2565 2566 |
# File 'app/models/delivery.rb', line 2564 def has_dropship_items? line_items.joins(:item).where(items: { dropship: true }).present? end |
#has_future_release_date? ⇒ Boolean
729 730 731 |
# File 'app/models/delivery.rb', line 729 def has_future_release_date? future_release_date && (future_release_date > Date.current) end |
#has_kits? ⇒ Boolean
741 742 743 |
# File 'app/models/delivery.rb', line 741 def has_kits? line_items_with_counters.any? { |li| li.children_count.to_i.positive? } end |
#has_kits_or_serial_numbers? ⇒ Boolean
745 746 747 |
# File 'app/models/delivery.rb', line 745 def has_kits_or_serial_numbers? has_serial_numbers? || has_kits? end |
#has_not_changed_but_has_shipping_lines? ⇒ Boolean
2120 2121 2122 |
# File 'app/models/delivery.rb', line 2120 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
3590 3591 3592 |
# File 'app/models/delivery.rb', line 3590 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
737 738 739 |
# File 'app/models/delivery.rb', line 737 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)
2747 2748 2749 2750 2751 |
# File 'app/models/delivery.rb', line 2747 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
2124 2125 2126 |
# File 'app/models/delivery.rb', line 2124 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
2556 2557 2558 |
# File 'app/models/delivery.rb', line 2556 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
2568 2569 2570 |
# File 'app/models/delivery.rb', line 2568 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
3190 3191 3192 |
# File 'app/models/delivery.rb', line 3190 def has_valid_shipments? shipments.packed_or_measured.present? && shipments.packed_or_measured.all?(&:has_dimensions?) end |
#incurs_oversized_penalty? ⇒ Boolean
3643 3644 3645 |
# File 'app/models/delivery.rb', line 3643 def incurs_oversized_penalty? (ships_ltl_freight? != true) && (is_warehouse_pickup? != true) && shipments.packed_or_measured.any? { |s| s.incurs_oversized_penalty? } end |
#index ⇒ Object
1228 1229 1230 |
# File 'app/models/delivery.rb', line 1228 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.
3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 |
# File 'app/models/delivery.rb', line 3439 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 = "#{Time.current}: Exception!!! Processing delivery id: #{id}, ref/name: #{name}: #{e}" logger.error logger.error e end end end |
#instant_quote? ⇒ Boolean
2509 2510 2511 |
# File 'app/models/delivery.rb', line 2509 def instant_quote? resource.respond_to?(:opportunity) && resource.opportunity && (resource.opportunity.opportunity_reception_type == 'IQ') end |
#instantiate_shipping_insurance ⇒ Object
3853 3854 3855 3856 3857 3858 3859 |
# File 'app/models/delivery.rb', line 3853 def instantiate_shipping_insurance if ships_ltl_freight? Shipping::LtlShippingInsurance.new else Shipping::PackageShippingInsurance.new end end |
#insured_shipments ⇒ Object
753 754 755 |
# File 'app/models/delivery.rb', line 753 def insured_shipments shipments.label_complete.where(is_ship_insured: true) end |
#insured_value ⇒ Object
1871 1872 1873 |
# File 'app/models/delivery.rb', line 1871 def insured_value chosen_shipping_method&.insured_value end |
#invoices ⇒ ActiveRecord::Relation<Invoice>
118 |
# File 'app/models/delivery.rb', line 118 has_many :invoices |
#is_amazon_seller_central? ⇒ Object
Alias for Customer#is_amazon_seller_central?
156 |
# File 'app/models/delivery.rb', line 156 delegate :catalog, :is_amazon_seller_central?, to: :customer |
#is_amazon_seller_central_veeqo? ⇒ Boolean
2067 2068 2069 |
# File 'app/models/delivery.rb', line 2067 def is_amazon_seller_central_veeqo? is_amazon_seller_central? && override_shipping_method? end |
#is_cross_border? ⇒ Boolean
3367 3368 3369 |
# File 'app/models/delivery.rb', line 3367 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
2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 |
# File 'app/models/delivery.rb', line 2071 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
3359 3360 3361 |
# File 'app/models/delivery.rb', line 3359 def is_domestic? origin_address && destination_address && origin_address.country_iso3 == destination_address.country_iso3 end |
#is_international? ⇒ Boolean
3363 3364 3365 |
# File 'app/models/delivery.rb', line 3363 def is_international? !is_domestic? end |
#is_international_and_ups_or_fedex? ⇒ Boolean
3371 3372 3373 |
# File 'app/models/delivery.rb', line 3371 def is_international_and_ups_or_fedex? is_international? && %w[UPS FedEx].include?(carrier) end |
#is_part_of_manifest? ⇒ Boolean
3194 3195 3196 |
# File 'app/models/delivery.rb', line 3194 def is_part_of_manifest? shipments.any?(&:manifest) || shipments.any?(&:speedee_manifest_shipment) end |
#is_rma_return ⇒ Object Also known as: is_rma_return?
2048 2049 2050 |
# File 'app/models/delivery.rb', line 2048 def is_rma_return rma_for_return.present? end |
#is_smart_service? ⇒ Boolean
2053 2054 2055 |
# File 'app/models/delivery.rb', line 2053 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
2014 2015 2016 2017 2018 2019 |
# File 'app/models/delivery.rb', line 2014 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
3816 3817 3818 |
# File 'app/models/delivery.rb', line 3816 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_entries ⇒ ActiveRecord::Relation<ItemLedgerEntry>
123 |
# File 'app/models/delivery.rb', line 123 has_many :item_ledger_entries |
#ledger_transactions ⇒ ActiveRecord::Relation<LedgerTransaction>
122 |
# File 'app/models/delivery.rb', line 122 has_many :ledger_transactions |
#line_allocation_status_hash ⇒ Object
832 833 834 835 836 837 838 |
# File 'app/models/delivery.rb', line 832 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_discounts ⇒ ActiveRecord::Relation<LineDiscount>
112 |
# File 'app/models/delivery.rb', line 112 has_many :line_discounts, through: :line_items |
#line_items ⇒ ActiveRecord::Relation<LineItem>
111 |
# File 'app/models/delivery.rb', line 111 has_many :line_items, extend: LineItemExtension, autosave: true, dependent: :nullify |
#line_items_eligible_for_packing ⇒ Object
778 779 780 |
# File 'app/models/delivery.rb', line 778 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_number ⇒ Object
3271 3272 3273 |
# File 'app/models/delivery.rb', line 3271 def line_items_requiring_serial_number line_items.reject(&:marked_for_destruction?).select(&:require_serial_number?) end |
#line_items_with_counters ⇒ Object
733 734 735 |
# File 'app/models/delivery.rb', line 733 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
828 829 830 |
# File 'app/models/delivery.rb', line 828 def lines_overallocated? line_allocation_status_hash.values.any?(&:negative?) end |
#link_serial_numbers_to_line_items ⇒ Object
3566 3567 3568 |
# File 'app/models/delivery.rb', line 3566 def link_serial_numbers_to_line_items line_items.each(&:link_serial_numbers) end |
#linked_return_deliveries ⇒ Object
2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 |
# File 'app/models/delivery.rb', line 2790 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
2102 2103 2104 |
# File 'app/models/delivery.rb', line 2102 def locked_for_fba? order&.is_fba? && order&.shipment_reference_number =~ CustomerConstants::AMAZON_FBA_ID_REGEX && shipments.any? && shipments.all? { |s| s.locked_for_fba? } end |
#ltl_freight_has_changed? ⇒ Boolean
2132 2133 2134 2135 |
# File 'app/models/delivery.rb', line 2132 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 |
#mark_multi_shipments_manifested(manifest, shipment) ⇒ Object
3582 3583 3584 |
# File 'app/models/delivery.rb', line 3582 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
2646 2647 2648 2649 2650 |
# File 'app/models/delivery.rb', line 2646 def matches?(existing_item, item, quantity) existing_item.line_item.nil? && existing_item.item == item && existing_item.unit_quantity == quantity end |
#messaging_logs ⇒ ActiveRecord::Relation<MessagingLog>
113 |
# File 'app/models/delivery.rb', line 113 has_many :messaging_logs, dependent: :destroy, as: :resource |
#name(short = false, filename = false) ⇒ Object
1232 1233 1234 1235 1236 1237 1238 1239 1240 |
# File 'app/models/delivery.rb', line 1232 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) ⇒ Object
2773 2774 2775 2776 2777 |
# File 'app/models/delivery.rb', line 2773 def new_shipment_attributes=(shipment_attributes) shipment_attributes.each do |attributes| shipments.build(attributes) end end |
#no_empty_shipments ⇒ Object
3600 3601 3602 3603 3604 |
# File 'app/models/delivery.rb', line 3600 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_store ⇒ Object
774 775 776 |
# File 'app/models/delivery.rb', line 774 def notify_store store.notify_of_new_delivery(self) end |
#open_activities_counter ⇒ Object
717 718 719 |
# File 'app/models/delivery.rb', line 717 def open_activities_counter all_activities.open_activities.count end |
#order ⇒ Order
97 |
# File 'app/models/delivery.rb', line 97 belongs_to :order, inverse_of: :deliveries, optional: true |
#origin_address ⇒ Address
Validations:
102 |
# File 'app/models/delivery.rb', line 102 belongs_to :origin_address, class_name: 'Address', validate: true, optional: true |
#override_shipping_method? ⇒ Boolean
1969 1970 1971 |
# File 'app/models/delivery.rb', line 1969 def override_shipping_method? chosen_shipping_method&.is_override? end |
#packable_active_lines ⇒ Object
3198 3199 3200 |
# File 'app/models/delivery.rb', line 3198 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) ⇒ Object
3202 3203 3204 3205 3206 |
# File 'app/models/delivery.rb', line 3202 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_hash ⇒ Object
3208 3209 3210 |
# File 'app/models/delivery.rb', line 3208 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) ⇒ Object
3212 3213 3214 |
# File 'app/models/delivery.rb', line 3212 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?
2044 2045 2046 |
# File 'app/models/delivery.rb', line 2044 def packageable? shipments.packageable.present? end |
#pallet_weight_matching ⇒ Object
3606 3607 3608 3609 3610 |
# File 'app/models/delivery.rb', line 3606 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 |
#pick_slip_file_name(with_extension = true) ⇒ Object
900 901 902 903 904 905 906 907 |
# File 'app/models/delivery.rb', line 900 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
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 |
# File 'app/models/delivery.rb', line 910 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 |
#po_number ⇒ Object
Alias for Order#po_number
158 |
# File 'app/models/delivery.rb', line 158 delegate :po_number, to: :order, allow_nil: true |
#precreated_rma ⇒ Rma
107 |
# File 'app/models/delivery.rb', line 107 has_one :precreated_rma, class_name: 'Rma', foreign_key: 'precreate_from_delivery_id', dependent: :nullify |
#preferred_shipping_option ⇒ Object
These methods below are from the model previously known as delivery_quote
1244 1245 1246 1247 1248 1249 1250 1251 |
# File 'app/models/delivery.rb', line 1244 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_requester ⇒ Party
124 |
# File 'app/models/delivery.rb', line 124 belongs_to :prepack_requester, class_name: 'Party', optional: true |
#preset_jobs ⇒ ActiveRecord::Relation<PresetJob>
119 |
# File 'app/models/delivery.rb', line 119 has_many :preset_jobs, inverse_of: :order |
#primary_party ⇒ Object
Alias for Resource_or_rma_for_delivery#primary_party
154 |
# File 'app/models/delivery.rb', line 154 delegate :customer, :primary_party, to: :resource_or_rma_for_delivery |
#print_container_label? ⇒ Boolean
630 631 632 633 634 635 636 637 638 639 640 641 642 643 |
# File 'app/models/delivery.rb', line 630 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 |
#quantities_remaining_to_allocate ⇒ Object
816 817 818 |
# File 'app/models/delivery.rb', line 816 def quantities_remaining_to_allocate line_allocation_status_hash.values.sum end |
#quote ⇒ Quote
96 |
# File 'app/models/delivery.rb', line 96 belongs_to :quote, inverse_of: :deliveries, optional: true |
#ready_to_choose_ships_economy_carrier? ⇒ Boolean
3833 3834 3835 |
# File 'app/models/delivery.rb', line 3833 def ready_to_choose_ships_economy_carrier? ships_economy_package? && pending_ship_labels? end |
#ready_to_print_amazon_fba_line_items ⇒ Object
3586 3587 3588 |
# File 'app/models/delivery.rb', line 3586 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! ⇒ Object
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 |
# File 'app/models/delivery.rb', line 1209 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_number ⇒ Object Also known as: to_s
841 842 843 |
# File 'app/models/delivery.rb', line 841 def reference_number "DE#{id}" end |
#reference_number_for_label ⇒ Object
3322 3323 3324 3325 3326 3327 3328 |
# File 'app/models/delivery.rb', line 3322 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_numbers ⇒ Object
3562 3563 3564 |
# File 'app/models/delivery.rb', line 3562 def rejoin_serial_numbers line_items.select(&:require_reservation?).each(&:rejoin_serial_numbers) end |
#relevant_changes ⇒ Object
2128 2129 2130 |
# File 'app/models/delivery.rb', line 2128 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 |
#relink_payments ⇒ Object
3405 3406 3407 |
# File 'app/models/delivery.rb', line 3405 def relink_payments Payment.where(id: payment_ids).update_all(delivery_id: id) end |
#remap_legacy_shipping_options_if_any ⇒ Object
3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 |
# File 'app/models/delivery.rb', line 3544 def 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_carrier ⇒ Object
1973 1974 1975 |
# File 'app/models/delivery.rb', line 1973 def reported_carrier (carrier || (override_shipping_method? && (shipments&.completed&.top_level&.first&.carrier || chosen_shipping_method&.description_override))).presence end |
#reported_master_tracking_number ⇒ Object
1988 1989 1990 |
# File 'app/models/delivery.rb', line 1988 def reported_master_tracking_number (master_tracking_number || (ltl_pro_number || carrier_bol || (override_shipping_method? && shipments&.completed&.top_level&.first&.tracking_number))).presence end |
#requires_manifest_completion? ⇒ Boolean
3719 3720 3721 |
# File 'app/models/delivery.rb', line 3719 def requires_manifest_completion? CARRIERS_REQUIRING_MANIFEST_COMPLETION.include?(carrier) end |
#reserved_serial_numbers ⇒ ActiveRecord::Relation<ReservedSerialNumber>
120 |
# File 'app/models/delivery.rb', line 120 has_many :reserved_serial_numbers, through: :line_items |
#reset_early_label_flag_on_order ⇒ Object
Reset purchase_label_early flag on the order so next ship-label goes through normal flow
3166 3167 3168 3169 3170 3171 3172 |
# File 'app/models/delivery.rb', line 3166 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_ship_labeled_at ⇒ Object
3244 3245 3246 |
# File 'app/models/delivery.rb', line 3244 def reset_ship_labeled_at update_attribute(:ship_labeled_at, nil) if ship_labeled_at.present? end |
#reset_shipping_cost ⇒ Object
1890 1891 1892 1893 |
# File 'app/models/delivery.rb', line 1890 def reset_shipping_cost clear_shipping_costs_safely self.shipping_cost = 0 if respond_to? :shipping_cost # REFACTOR end |
#reset_ships_economy_if_unselected ⇒ Object
3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 |
# File 'app/models/delivery.rb', line 3765 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 do |sc| sc.name end.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 { |sc| sc.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 |
#resource ⇒ Object
682 683 684 |
# File 'app/models/delivery.rb', line 682 def resource order || quote end |
#resource_or_rma_for_delivery ⇒ Object
686 687 688 |
# File 'app/models/delivery.rb', line 686 def resource_or_rma_for_delivery resource || rma_for_return end |
#resource_present ⇒ Object
721 722 723 |
# File 'app/models/delivery.rb', line 721 def resource_present resource.present? end |
#resource_shipping_method ⇒ Object
3861 3862 3863 3864 3865 3866 3867 3868 |
# File 'app/models/delivery.rb', line 3861 def resource_shipping_method if resource.present? resource.shipping_method elsif is_rma_return? && rma_for_return.present? # For RMA returns, always use 'ground' as the shipping method 'ground' end end |
#retrieve_friendly_shipping_method(show_customer_pays_info = false, for_www = false, sc = nil, with_delivery_commitment = false, for_edi = false) ⇒ Object
rescue "n/a"
1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 |
# File 'app/models/delivery.rb', line 1926 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 if (begin sc.shipping_account_number rescue StandardError false end) && show_customer_pays_info customer_pays = " (cust. acct.: #{sc.shipping_account_number.account_number})" unless for_www customer_pays = " (using your linked #{carrier} account: #{sc.shipping_account_number.account_number})" if for_www end if for_edi == false && sc&.shipping_option&.carrier == 'FedEx' && sc&.insured_value.to_f >= 500.0 && !signature_confirmation # flag this unless we already have signature_confirmation set notes += ' (FedEx automatically requires Direct Signature for all declared value shipments of $500 or more)' end elsif shipping_line_item shipping_method_name = shipping_line_item.name end end "#{shipping_method_name}#{mps}#{method_cod}#{customer_pays}#{ret}#{notes}".strip end |
#retrieve_shipping_costs(rate_ship_date: nil) ⇒ Object
Retrieve shipping costs from carriers
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 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 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 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 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 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 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 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 |
# File 'app/models/delivery.rb', line 1259 def retrieve_shipping_costs(rate_ship_date: nil) # Use advisory lock to prevent concurrent processing and infinite loops lock_key = "delivery|#{id}|retrieve_shipping_costs" result = self.class.with_advisory_lock_result(lock_key, timeout_seconds: 0) do # Store the rate_ship_date for use later in this method @rate_ship_date_override = rate_ship_date # puts "Delivery#retrieve_shipping_costs: self.resource.shipping_address_id: #{self.resource.shipping_address_id}" # puts "Delivery#retrieve_shipping_costs: self.destination_address.id: #{self.destination_address.id}, self.destination_address_id: #{self.destination_address_id}" return_code = :no_options = +'' = +'' = [] is_store_transfer_delivery = order.try(:is_store_transfer?) shipping_option if is_service_only? || is_warehouse_pickup? so = ShippingOption.where(name: 'override', country: resource.store.country.iso).first self.shipping_option_id = so.id old_override_shipping_cost = shipping_costs.detect { |sc| sc.shipping_option == so } cost_to_use = 0.0 if old_override_shipping_cost cost_to_use = old_override_shipping_cost.cost || 0.0 end # need something here cost_to_use = 0.0 if is_warehouse_pickup? cost_to_use = WAREHOUSE_PICKUP_FEE if apply_warehouse_fee? # we apply warehouse pickup fee only to first delivery clear_shipping_costs_safely shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use) shpcst.description_override = retrieve_shipping_description_for_shipping_cost(shpcst) shipping_costs << shpcst raise "Cannot create shipping line item as item is missing from shipping_option ID: #{so.id}" if so.item.nil? return_code = :ok elsif is_zero_charge_dropship? Rails.logger.debug 'is_zero_charge_dropship? true' so = ShippingOption.where(name: 'override', country: resource.store.country.iso).first self.shipping_option_id = so.id shipping_costs.detect { |sc| sc.shipping_option == so } cost_to_use = 0.0 clear_shipping_costs_safely shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use) shpcst.description_override = retrieve_shipping_description_for_shipping_cost(shpcst) shipping_costs << shpcst raise "Cannot create shipping line item as item is missing from shipping_option ID: #{so.id}" if so.item.nil? return_code = :ok # elsif is_rma_return? # # TBD REFACTOR NO ORDER DEPENDENCY FOR RETURN DELIVERY # # here we want just UPS ground shipping option with cost of 0.00 # if self.shipping_option_id.present? # so = self.shipping_option # else # country_iso = resource&.store&.country&.iso || rma_for_return&.customer&.store&.country&.iso # ups_ground = 'ground' # ups_ground = 'standard' if country_iso == 'CA' # so = ShippingOption.active.where(name: ups_ground, country: country_iso).first || ShippingOption.where(name: ups_ground, country: country_iso).first # self.shipping_option_id = so.id # end # cost_to_use = 0.0 # shipping_costs.delete_all # shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use) # shpcst.description_override = retrieve_shipping_description_for_shipping_cost(shpcst) # shipping_costs << shpcst # raise "Cannot create shipping line item as item is missing from shipping_option ID: #{so.id}" if so.item.nil? # return_code = :ok elsif customer && (destination_address || resource.installation_postal_code) # test if we want to default to ltl freight, here the ltl_freight.nil? is important because it means the user did not explcitly set it to false, so it's true no need to set it true, or it's false which means it was manually and explicitly set to false so we 'stick' with the selected shipping option even if the weight increases past the default LTL threshold Rails.logger.debug { "retrieve_shipping_costs: ltl_freight: #{ltl_freight}, ltl_freight_guaranteed: #{ltl_freight_guaranteed}, order&.cart?: #{order&.cart?}, is_default_ltl_freight?: #{is_default_ltl_freight?}" } if (ltl_freight.nil? || order&.cart?) && !ltl_freight_guaranteed && is_default_ltl_freight? # only do this for when no shipping method has been chosen, i.e. no shipping costs present or none matching or is cart and freight option not already set self.ltl_freight = true end if (ltl_freight || ltl_freight_guaranteed) && order&.cart? && !is_default_ltl_freight? # also reset ltl freight for www carts and when freight is no longer relevant self.ltl_freight = false self.ltl_freight_guaranteed = false end # Heavyweight method call below. if ltl_freight_changed? shipments.pallets.packed.each(&:unpack) unless ltl_freight # here we are moving from LTL to Package, unpack pallets # we reset selected shipping options because this is like a new shipping calculation and we want sensible defaults, not override update_column(:selected_shipping_cost_id, nil) update_column(:shipping_option_id, nil) end = (www: order.try(:is_www)) packages_arr = [] [:shipping_weights].each_with_index do |weight, i| packages_arr << { length: [:shipping_dimensions][i][0].to_f, width: [:shipping_dimensions][i][1].to_f, height: [:shipping_dimensions][i][2].to_f, weight: weight.to_f, flat_rate_package_type: [:flat_rate_package_types][i], container_type: [:container_types][i] } end carrier_responses_hash = { date: Time.current.to_s, packages: packages_arr, origin_address: get_address_hash_from_address([:origin_address]), destination_address: (if [:destination_address].present? get_address_hash_from_address([:destination_address]) else { postal_code: [:postal_code], state_code: [:state], country_iso: [:country_iso] } end ), rates: [] } [:delivery] = self # Calculate effective ship date for rate requests # This is when we plan to ship - user can override, otherwise defaults to: # - Today if within business hours # - Next business day if after hours # Note: requested_ship_before is the retailer's deadline (informational only, not used for rate calculation) company = order&.company || resource&.company || rma_for_return&.company raise "Cannot calculate shipping - no company associated with order/resource/rma" unless company # Use user-provided rate_ship_date if valid, otherwise calculate default ship_date = @rate_ship_date_override if @rate_ship_date_override && @rate_ship_date_override >= Date.current ship_date ||= company.next_valid_ship_date # For LTL freight, ship date must be at least next business day (pickup scheduling required) if ltl_freight || ltl_freight_guaranteed next_business_day = company.next_business_day(Date.current + 1.day) if ship_date < next_business_day ship_date = next_business_day Rails.logger.debug { "retrieve_shipping_costs: LTL requires pickup scheduling, adjusted ship_date to #{ship_date}" } end end [:ship_date] = ship_date Rails.logger.debug { "retrieve_shipping_costs: using ship_date=#{ship_date} (override=#{@rate_ship_date_override.present?})" } # Add Walmart "Ship with Walmart" info for Walmart marketplace orders # This enables discounted shipping rate comparison from Walmart's carrier partnerships if order&.edi_orchestrator_partner&.start_with?('walmart_seller') [:walmart_order_info] = { partner: order.edi_orchestrator_partner.to_sym, po_number: order.edi_po_number, deliver_by_date: order.requested_deliver_by, ship_by_date: order.requested_ship_before || Date.current } end # Add Amazon Buy Shipping info for Amazon marketplace orders # This enables Amazon's negotiated rates with A-to-Z Buy Shipping protections if order&.edi_orchestrator_partner&.start_with?('amazon_seller') [:amazon_order_info] = { partner: order.edi_orchestrator_partner.to_sym, order_id: order.edi_po_number, deliver_by_date: order.requested_deliver_by, ship_by_date: ship_date } end # Pass catalog for carrier exclusion filtering in WyShipping [:catalog] = catalog if order&.customer&.catalog_id.present? = WyShipping.() Rails.logger.debug { "Delivery id: #{id}, retrieve_shipping_costs, shipping_options: #{.inspect}" } if .any? << .first[:status_description] if .first[:status_code] == '1' += .first[:packages].inspect.to_s # [0..149] # return_message += "..." if return_message.length > 149 so_override = is_rma_return? ? ShippingOption.find_by(name: 'override', country: rma_for_return&.customer&.store&.country&.iso) : ShippingOption.find_by(name: 'override', country: resource.store.country.iso) old_override_shipping_cost = shipping_costs.detect { |sc| sc.shipping_option_id == so_override&.id } cost_override = nil cost_override = old_override_shipping_cost.cost if old_override_shipping_cost cost_override = get_economy_shipping_cost_to_use if ships_economy_package? cost_override = get_fallback_cost_to_use if ships_economy_ltl? # Capture intent before deleting: if the ONLY existing cost is the auto-set override # (i.e. a previous failed request left no real options), we can release the override # lock when valid rates come back. If non-override costs already existed alongside the # override, the user deliberately chose override and we must respect that choice. override_was_only_option = shipping_option&.is_override? && shipping_costs.skip_override.empty? shipping_costs.skip_override.delete_all # Preload all ShippingOption records needed for this batch to avoid N+1 queries. # Many option names exist for multiple countries (US, CA, CN, etc.). # Scope to the store's origin country first so index_by always picks the # correct variant; fall back to any country if no origin-country record exists. all_option_names = .last.map { |opt, val| (val[:option_name] || opt).to_s }.uniq origin_country_iso = is_rma_return? ? rma_for_return&.customer&.store&.country&.iso : resource.store.country.iso so_by_name = ShippingOption.where(name: all_option_names, country: origin_country_iso).index_by(&:name) # Collect all new shipping cost records for batch insert (avoids 12+ individual transactions) now = Time.current batch_attrs = [] .last.each do |option, value| san_id = nil if bill_shipping_to_customer && origin_address.supports_third_party_shipping san = customer.shipping_account_number(value[:carrier], (begin resource.billing_address rescue StandardError nil end), destination_address) san_id = san.id if san end option_name = (value[:option_name] || option).to_s so = so_by_name[option_name] || ShippingOption.find_by(name: option_name, country: origin_country_iso) || ShippingOption.find_by(name: option_name) cod = true if cod_collection_type.present? # handle origin address supports_third_party_shipping varies from supplier to supplier if is_store_transfer_delivery || so.is_third_party_only? cost_to_use = 0.0 transportation_charges = 0.0 = 0.0 else cost_to_use = value[:cost] transportation_charges = value[:transportation_charges] = value[:service_options_charges] insured_value = value[:insured_value] end batch_attrs << { delivery_id: id, shipping_option_id: so.id, cost: cost_to_use, transportation_charges: transportation_charges || 0.0, service_options_charges: || 0.0, saturday_delivery: saturday_delivery || false, signature_confirmation: signature_confirmation || false, cod: cod || false, shipping_account_number_id: san_id, insured_value: insured_value, description_override: (so.is_freightquote? ? "Freightquote #{value[:service_code]}" : nil), rate_data: (value[:rate_data] || {}).merge({ actual_cost: value[:cost].to_f.round(2) }), created_at: now, updated_at: now } # Use .to_f before rounding — BigDecimal serializes as a String in JSONB, causing # sort/comparison failures when the column is read back (same pattern as line 1502). rate_entry = value.merge({ cost: cost_to_use.to_f.round(2), transportation_charges: value[:transportation_charges].to_f.round(2), service_options_charges: value[:service_options_charges].to_f.round(2) }) # Prefix marketplace rates so they're distinguishable from direct carrier rates if value[:carrier] == 'WalmartSeller' rate_entry[:service_name] = "Ship with Walmart: #{rate_entry[:service_name]}" elsif value[:carrier] == 'AmazonSeller' ptp_carrier = value.dig(:rate_data, :amz_carrier_id) || value.dig(:rate_data, 'amz_carrier_id') if ptp_carrier.present? && ptp_carrier != 'AMZN_US' rate_entry[:service_name] = "AMZ #{rate_entry[:service_name]}" else rate_entry[:service_name] = "Amazon Shipping: #{rate_entry[:service_name]}" end end carrier_responses_hash[:rates] << rate_entry end fk_error = false if batch_attrs.any? begin ShippingCost.insert_all(batch_attrs) rescue ActiveRecord::InvalidForeignKey => e Rails.logger.warn "Delivery#retrieve_shipping_costs: FK violation for delivery #{id} — #{e.}" fk_error = true end end unless fk_error shipping_costs.reload return_code = :ok if shipping_costs.any? # Release the override lock when valid rates come back AND the override was auto-set # (the only cost before this run was the placeholder override from a prior failed request). # If the user had already picked override while other options existed, non-override costs # would have been present before the delete above — override_was_only_option stays false # and we leave the intentional selection untouched. # EDI orders where the label is purchased externally always keep override regardless. if return_code == :ok && override_was_only_option && order&.edi_shipping_option_name != 'override' self.shipping_option_id = nil end unless shipping_costs.detect { |sc| sc.shipping_option_id == so_override&.id } if is_store_transfer_delivery cost_override = 0.0 else cost_override ||= (shipping_costs.detect { |sc| sc.shipping_option_id == shipping_option_id } || sorted_shipping_costs.first).try(:cost) || get_fallback_cost_to_use # need something here end shpcst = ShippingCost.new(shipping_option: so_override, cost: cost_override) shpcst.description_override = 'Shipping override, please confirm' shipping_costs << shpcst end sc_override = shipping_costs.detect { |sc| sc.shipping_option_id == so_override&.id } cost_override = get_economy_shipping_cost_to_use if ships_economy_package? cost_override = get_fallback_cost_to_use if ships_economy_ltl? sc_override.update(cost: cost_override) if sc_override end else # nothing comes back, invalid preference or option set like signature confirmation or saturday delivery, in this case just set override of $500 and let rep sort it out: we need some shipping option/cost. If europe, override is 0 euro so = is_rma_return? ? ShippingOption.find_by(name: 'override', country: rma_for_return&.customer&.store&.country&.iso) : ShippingOption.where(name: 'override', country: resource.store.country.iso).first self.shipping_option_id = so.id clear_shipping_costs_safely cost_to_use = if is_rma_return? rma_for_return&.customer&.store&.country&.eu_country? ? 0 : get_fallback_cost_to_use else resource.store.country.eu_country? ? 0 : get_fallback_cost_to_use end shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use) shpcst.description_override = 'Shipping override, please confirm' shipping_costs << shpcst return_code = :no_options << "No valid shipping options could be found for #{name}. Setting override of $#{get_fallback_cost_to_use}." end else # shipping service unavailable # shipping services unavailable, in this case just set override of $500 and let rep sort it out: we need some shipping option/cost. If europe, override is 0 euro so = is_rma_return? ? ShippingOption.find_by(name: 'override', country: rma_for_return&.customer&.store&.country&.iso) : ShippingOption.where(name: 'override', country: resource.store.country.iso).first self.shipping_option_id = so.id clear_shipping_costs_safely cost_to_use = resource.store.country.eu_country? ? 0 : get_fallback_cost_to_use shpcst = ShippingCost.new(shipping_option: so, cost: cost_to_use) shpcst.description_override = 'Shipping override, please confirm' shipping_costs << shpcst return_code = :no_response = "There was no response from carrier web services for #{name}. Setting override of $#{get_fallback_cost_to_use}, please try again or request assistance." end end result = { code: return_code, message: , packages: , created_at: Time.current.to_fs(:db) } # Persist the human/debug message via messaging_logs (see Models::LegacyRateRequest) begin messaging_logs.create!(category: 'shipping_rate_request', message: result) rescue StandardError # non-fatal; continue end # Assign carrier_responses in-memory self.carrier_responses = carrier_responses_hash || {} # Avoid triggering before_save callback work if nothing changed materially beyond carrier_responses if is_rma_return? && changes.except('carrier_responses').empty? update_columns(carrier_responses: self.carrier_responses) else save end resource.shipping_issue_alerted = false if resource.present? result end # If lock was not acquired, return already processing message unless result.lock_was_acquired? return { code: :already_processing, message: 'Shipping costs are already being retrieved', packages: '' } end # Return the result from the block result.result end |
#retrieve_shipping_description_for_line_item(shipping_line) ⇒ Object
2021 2022 2023 |
# File 'app/models/delivery.rb', line 2021 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) ⇒ Object
2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 |
# File 'app/models/delivery.rb', line 2025 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&.shipping_account_number&.account_number})" if sc&.shipping_account_number.present? 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) ⇒ Object
1736 1737 1738 1739 1740 1741 1742 1743 |
# File 'app/models/delivery.rb', line 1736 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_return ⇒ Rma
108 |
# File 'app/models/delivery.rb', line 108 has_one :rma_for_return, class_name: 'Rma', foreign_key: 'return_delivery_id', dependent: :nullify |
#save_purchase_order_if_needed(po) ⇒ Object
2671 2672 2673 |
# File 'app/models/delivery.rb', line 2671 def save_purchase_order_if_needed(po) po.save! unless po.purchase_order_items.empty? end |
#schedule_pickup_if_necessary ⇒ Object
3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 |
# File 'app/models/delivery.rb', line 3805 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_packaging ⇒ Object
3761 3762 3763 |
# File 'app/models/delivery.rb', line 3761 def schedule_request_estimated_packaging DeliveryRequestPrePackWorker.perform_in(5.seconds, id) end |
#selected_shipping_cost ⇒ ShippingCost
100 |
# File 'app/models/delivery.rb', line 100 belongs_to :selected_shipping_cost, class_name: 'ShippingCost', optional: true |
#send_address_type_issue_notification ⇒ Object
2675 2676 2677 |
# File 'app/models/delivery.rb', line 2675 def send_address_type_issue_notification InternalMailer.address_type_issue_notification(self).deliver end |
#send_canada_post_manual_void_email(tracking_numbers) ⇒ Object
2693 2694 2695 |
# File 'app/models/delivery.rb', line 2693 def send_canada_post_manual_void_email(tracking_numbers) Mailer.canada_post_manual_void_email(self, tracking_numbers).deliver end |
#send_commercial_invoice_to_carrier ⇒ Object
2701 2702 2703 |
# File 'app/models/delivery.rb', line 2701 def send_commercial_invoice_to_carrier InternalMailer.commercial_invoice_to_carrier(self).deliver if should_send_commercial_invoice_to_carrier? end |
#send_delivery_pre_pack_cancelled_notification(cancelled_by: nil) ⇒ Object
2688 2689 2690 2691 |
# File 'app/models/delivery.rb', line 2688 def send_delivery_pre_pack_cancelled_notification(cancelled_by: nil) update_column(:suggested_packaging_text, nil) InternalMailer.delivery_pre_pack_cancelled_notification(self, cancelled_by: cancelled_by).deliver_later end |
#send_delivery_pre_packed_notification ⇒ Object
2683 2684 2685 2686 |
# File 'app/models/delivery.rb', line 2683 def send_delivery_pre_packed_notification update_column(:suggested_packaging_text, nil) # reset this text field after pre-pack InternalMailer.delivery_pre_packed_notification(self).deliver_later end |
#send_dropship_delivery_notification ⇒ Object
2679 2680 2681 |
# File 'app/models/delivery.rb', line 2679 def send_dropship_delivery_notification InternalMailer.dropship_delivery_notification(self).deliver end |
#send_purolator_manual_void_email(tracking_numbers) ⇒ Object
2697 2698 2699 |
# File 'app/models/delivery.rb', line 2697 def send_purolator_manual_void_email(tracking_numbers) Mailer.purolator_manual_void_email(self, tracking_numbers).deliver end |
#serial_numbers_file_name ⇒ Object
985 986 987 |
# File 'app/models/delivery.rb', line 985 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_print ⇒ Object
989 990 991 992 993 994 995 996 |
# File 'app/models/delivery.rb', line 989 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_cogs ⇒ Object
2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 |
# File 'app/models/delivery.rb', line 2539 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_needed ⇒ Object
3494 3495 3496 3497 3498 3499 3500 3501 |
# File 'app/models/delivery.rb', line 3494 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) ⇒ Object
3711 3712 3713 3714 3715 3716 3717 |
# File 'app/models/delivery.rb', line 3711 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 = {}) ⇒ Object
3216 3217 3218 |
# File 'app/models/delivery.rb', line 3216 def set_packaged_items_md5_hash( = {}) Shipping::DeliveryMd5Extractor.new().process(self) end |
#set_proper_shipping_cost ⇒ Object
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 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 |
# File 'app/models/delivery.rb', line 2137 def set_proper_shipping_cost shipping_lines = line_items.select(&:is_shipping?) shipping_line = shipping_lines.pop Rails.logger.debug { "set_proper_shipping_cost, shipping_line: #{shipping_line ? shipping_line.id : 'none'}" } skip = false Rails.logger.debug { "set_proper_shipping_cost, self.changes: #{changes}" } Rails.logger.debug { "set_proper_shipping_cost, self.relevant_changes: #{relevant_changes}" } Rails.logger.debug { "set_proper_shipping_cost, self.has_not_changed_but_has_shipping_lines?: #{has_not_changed_but_has_shipping_lines?}" } Rails.logger.debug { "set_proper_shipping_cost, shipping_costs.select{|sc| sc.changes.any?}.empty?: #{shipping_costs.select { |sc| sc.changes.any? }.empty?}" } if has_not_changed_but_has_shipping_lines? && shipping_costs.select { |sc| sc.changes.any? }.empty? skip = true end # in the before_save context only set_proper_shipping_cost if we have not changed but have shipping lines Rails.logger.debug { "set_proper_shipping_cost, self.has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line): #{has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line)}" } if has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line) skip = false end # this tests to see if we do have shipping cost ids but none of them match the shipping line's shipping cost id: this means we are out of synch and the shipping line must be updated Rails.logger.debug { "set_proper_shipping_cost, resource&.do_not_detect_shipping: #{resource&.do_not_detect_shipping}" } skip = true if resource&.do_not_detect_shipping Rails.logger.debug { "set_proper_shipping_cost, self.shipping_costs.present?: #{shipping_costs.present?}" } skip = true if shipping_costs.blank? Rails.logger.debug { "set_proper_shipping_cost, skip: #{skip}" } Rails.logger.debug { "set_proper_shipping_cost, force_shipping_cost_update: #{force_shipping_cost_update}" } # Extra guard: if no trigger fields changed, a valid selection exists, and no shipping_costs mutated, skip heavy work if is_rma_return? && !skip trigger_keys = %w[destination_address_id shipping_option_id ltl_freight ltl_freight_guaranteed signature_confirmation saturday_delivery selected_shipping_cost_id] if (relevant_changes.keys & trigger_keys).empty? && selected_shipping_cost_id.present? && shipping_costs.select { |sc| sc.changes.any? }.empty? && !has_shipping_line_linked_to_shipping_cost_that_doesnt_exist_in_delivery?(shipping_line) skip = true end end return true if skip && force_shipping_cost_update != true # try first with selected_shipping_cost_id Rails.logger.debug { "set_proper_shipping_cost, selected_shipping_cost_id: #{selected_shipping_cost_id}" } Rails.logger.debug { "set_proper_shipping_cost, self.shipping_option.id: #{shipping_option&.id}" } Rails.logger.debug { "set_proper_shipping_cost, self.shipping_option.is_override?: #{shipping_option&.is_override?}" } Rails.logger.debug { "set_proper_shipping_cost, self.selected_shipping_cost&.shipping_option&.is_override?: #{selected_shipping_cost&.shipping_option&.is_override?}" } if selected_shipping_cost.present? && selected_shipping_cost&.shipping_option&.is_override? reset_override = false reset_override = true if relevant_changes.keys.include?('destination_address_id') || ltl_freight_has_changed? Rails.logger.debug { "set_proper_shipping_cost, (relevant_changes.keys.include?('destination_address_id') || ltl_freight_has_changed?)): #{relevant_changes.keys.include?('destination_address_id') || ltl_freight_has_changed?}" } # If the override is intentionally selected, never reset it regardless of address/LTL changes. # Check both: 1) delivery.shipping_option is override, 2) order's EDI shipping option is 'override' override_is_intentional = self.shipping_option&.is_override? || order&.edi_shipping_option_name == 'override' reset_override = false if override_is_intentional if reset_override Rails.logger.debug 'set_proper_shipping_cost, reset_override!!!' # go for cheapest if address changed or it switched from package to ltl freight or vice versa self.selected_shipping_cost_id = nil end end sorted_costs = sorted_shipping_costs(uniq_by_shipping_option_id: true, filter_by_ltl_freight: ships_ltl_freight?).compact shipping_cost_entry = sorted_costs.detect { |sc| sc.id == selected_shipping_cost_id } # need to do this because the selected_shipping_cost might not yet be save e.g. setting override and override cost in one step shipping_cost_was_auto_matched = shipping_cost_entry.nil? # Retrieves based on the resource or customer default if not set already # IMPORTANT: Don't override manually selected shipping options (especially overrides) with EDI preferred option # Preserve override selections even when shipping_option is temporarily nil during transitions if shipping_option.blank? # If there's already a selected shipping cost (especially an override), use its shipping option if selected_shipping_cost.present? && selected_shipping_cost.shipping_option.present? self.shipping_option = selected_shipping_cost.shipping_option # Only fall back to preferred_shipping_option if no selection exists else self.shipping_option = preferred_shipping_option end # If shipping_option is already set (including overrides), preserve it - don't change to preferred_shipping_option # This ensures manually selected overrides are preserved during order release from CR hold end Rails.logger.debug { "set_proper_shipping_cost, sorted_costs: #{sorted_costs.map { |sc| [sc.shipping_option.name, sc.id] }.inspect}" } # If switching to LTL, prefer the absolute cheapest freight option BEFORE any heuristic matches # This avoids picking an expensive carrier/service-level just because it matches previous preferences if ships_ltl_freight? && shipping_cost_entry.nil? freight_candidates = shipping_costs.select { |sp| sp.shipping_option.is_freight && !sp.hide? } freight_candidates_sorted = freight_candidates.sort_by { |a| a.cost.to_f.round(2) } Rails.logger.debug do "set_proper_shipping_cost, freight candidates by cost: #{freight_candidates_sorted.map { |sc| [sc.id, sc.shipping_option&.description, sc.cost.to_f.round(2)] }.inspect}" end cheapest_freight = freight_candidates_sorted.first if cheapest_freight Rails.logger.debug do "set_proper_shipping_cost, preselect cheapest freight: id=#{cheapest_freight.id}, #{cheapest_freight.shipping_option&.description}, cost=#{cheapest_freight.cost.to_f.round(2)}" end shipping_cost_entry = cheapest_freight end end # We have a selected shipping option, we try to match it or come close to it if shipping_cost_entry.nil? && self.shipping_option && !self.shipping_option.is_override? Rails.logger.debug { "set_proper_shipping_cost, self.shipping_option: #{self.shipping_option.inspect}" } # First try to match the selected shipping option if we have one shipping_cost_entry = sorted_costs.detect { |sc| sc.shipping_option == self.shipping_option } Rails.logger.debug do "set_proper_shipping_cost, after match by shipping_option: shipping_cost_entry.shipping_option: #{begin shipping_cost_entry.shipping_option.inspect rescue StandardError nil end}" end # If Nothing matched based on the shipping option, try to be smart and find a similar service level and carrier option shipping_cost_entry ||= sorted_costs.detect { |sc| (sc.shipping_option.carrier == self.shipping_option.carrier) && (sc.shipping_option.service_level == self.shipping_option.service_level) } if self.shipping_option.service_level.present? Rails.logger.debug do "set_proper_shipping_cost, after match by shipping_option service_level and/or carrier: shipping_cost_entry.shipping_option: #{begin shipping_cost_entry.shipping_option.inspect rescue StandardError nil end}" end # Still nothing? so try to find the closest shipping_option based on the same service level, different carrier shipping_cost_entry ||= sorted_costs.detect { |sc| sc.shipping_option.service_level == self.shipping_option.service_level } if self.shipping_option.service_level.present? Rails.logger.debug do "set_proper_shipping_cost, after match by shipping_option.service_level: shipping_cost_entry.shipping_option: #{begin shipping_cost_entry.shipping_option.inspect rescue StandardError nil end}" end # Nothing again? ok try somethign with the same carrier shipping_cost_entry ||= sorted_costs.detect { |sc| sc.shipping_option.carrier == self.shipping_option.carrier } if self.shipping_option.carrier.present? Rails.logger.debug do "set_proper_shipping_cost, after match by shipping_option.carrier: shipping_cost_entry.shipping_option: #{begin shipping_cost_entry.shipping_option.inspect rescue StandardError nil end}" end end if self.shipping_option.present? && self.shipping_option.is_override? Rails.logger.debug { "set_proper_shipping_cost, self.shipping_option: #{self.shipping_option.inspect}" } shipping_cost_entry ||= sorted_costs.detect { |sc| sc.shipping_option == self.shipping_option } end if ltl_freight_has_changed? override_shipping_cost_entry ||= sorted_costs.detect { |sc| sc.is_override? } override_shipping_cost_entry.cost = get_fallback_cost_to_use if relevant_changes['ltl_freight'].last == true override_shipping_cost_entry.cost = get_economy_shipping_cost_to_use if relevant_changes['ltl_freight'].last == false && ships_economy Rails.logger.debug { "set_proper_shipping_cost, LTL changes: #{relevant_changes['ltl_freight']}, shipping_cost_entry.cost: #{shipping_cost_entry&.cost}" } end # If LTL freight is active and nothing matched yet, as an additional guard prefer the cheapest freight option if ships_ltl_freight? shipping_cost_entry ||= sorted_shipping_costs_www_hash[:freight]&.first end # If there's a delivery deadline, check if current selection meets it - if not, find best option if order&.requested_deliver_by.present? deliver_by = order.requested_deliver_by current_est_date = shipping_cost_entry&.carrier_estimated_delivery_date current_is_late = current_est_date.present? && current_est_date > deliver_by current_is_override = shipping_cost_entry&.is_override? current_has_no_date = shipping_cost_entry.present? && current_est_date.blank? # IMPORTANT: Do NOT auto-replace selections when: # 1. The delivery's shipping_option is explicitly set to override (user chose it) # 2. The order's EDI shipping option is 'override' (EDI orders like Amazon non-Buy-Shipping) # 3. The delivery's shipping_option is a Ship with Walmart (SWW) option (Walmart orders) # 4. The delivery already has an AmazonSeller option selected (user chose a specific AMZBS rate) user_selected_override = self.shipping_option&.is_override? edi_requires_override = order&.edi_shipping_option_name == 'override' user_selected_sww = self.shipping_option&.carrier == 'WalmartSeller' edi_requires_sww = order&.edi_shipping_option_name == 'sww' || order&.edi_shipping_option_name&.start_with?('sww_') user_selected_amz_bs = self.shipping_option&.carrier == 'AmazonSeller' edi_requires_amz_bs = order&.edi_shipping_option_name == 'amzbs' || order&.edi_shipping_option_name&.start_with?('amzbs_') selection_is_intentional = user_selected_override || edi_requires_override || user_selected_sww || edi_requires_sww || user_selected_amz_bs # For AMZBS orders where no prior selection existed (auto-matched by # carrier heuristic), optimize to the cheapest on-time AMZBS rate. # This handles initial import where the carrier-match picks a suboptimal # rate (e.g., Amazon Shipping Ground at $22.93 when FedEx HD at $18.61 # is available and on-time). Skipped when the user already selected a # specific AMZBS rate (selection_is_intentional) — a rate refresh # orphans the old selected_shipping_cost_id making # shipping_cost_was_auto_matched true even though the user chose it. if edi_requires_amz_bs && shipping_cost_entry.present? && shipping_cost_was_auto_matched && !selection_is_intentional amzbs_costs = sorted_costs.select(&:is_amzbs?) amzbs_on_time = amzbs_costs.select { |sc| est = sc.carrier_estimated_delivery_date est.present? && est <= deliver_by }.sort_by { |sc| sc.cost.to_f } cheapest_amzbs = amzbs_on_time.first if cheapest_amzbs && cheapest_amzbs.cost.to_f < shipping_cost_entry.cost.to_f shipping_cost_entry = cheapest_amzbs Rails.logger.debug { "set_proper_shipping_cost, AMZBS auto-optimized to cheapest on-time: #{cheapest_amzbs.shipping_option&.name} at $#{cheapest_amzbs.cost}" } end end # Only find a better option if: # - No selection exists AND selection is NOT intentional, OR # - Current selection is late (but NOT if selection is intentional), OR # - Current selection is an unintentional override, OR # - Current selection has no delivery date (but NOT if selection is intentional) # IMPORTANT: If selection is intentional (override, SWW, or AMZBS), we should # NEVER auto-select a different option even if shipping_cost_entry is nil # (happens during refresh_deliveries_quoting before the ShippingCost is recreated) should_find_better_option = (shipping_cost_entry.nil? && !selection_is_intentional) || (current_is_late && !selection_is_intentional) || (current_is_override && !selection_is_intentional) || (current_has_no_date && !selection_is_intentional) if should_find_better_option # Filter to non-override options for this selection = sorted_costs.reject { |sc| sc.is_override? } # AMZBS orders must use Amazon Buy Shipping rates — Heatwave rates are # irrelevant because the label is purchased from Amazon's API. = .select { |sc| sc.is_amzbs? } if edi_requires_amz_bs on_time_candidates = .select do |sc| est_date = sc.carrier_estimated_delivery_date est_date.present? && est_date <= deliver_by end.sort_by { |sc| sc.cost.to_f } best_option = nil if on_time_candidates.any? # If customer has a third-party billing account, prefer options matching that carrier if customer&.shipping_account_numbers&.any? preferred_carriers = customer.shipping_account_numbers.map { |san| san.carrier }.uniq best_option = on_time_candidates.detect { |sc| preferred_carriers.include?(sc.shipping_option.carrier) } end # Otherwise just pick the cheapest on-time option best_option ||= on_time_candidates.first else # No on-time options - pick the cheapest non-override option best_option = .sort_by { |sc| sc.cost.to_f }.first end if best_option shipping_cost_entry = best_option Rails.logger.debug do "set_proper_shipping_cost, delivery deadline selection: deliver_by=#{deliver_by}, on_time=#{on_time_candidates.any?}, selected=#{shipping_cost_entry&.shipping_option&.description}" end end end end if resource.try(:cart?) # For carts, fall back default is the cheapest shipping_cost_entry ||= sorted_shipping_costs_www_hash[:economy]&.first Rails.logger.debug do "set_proper_shipping_cost, after fall back default to cheapest for cart: shipping_cost_entry.shipping_option: #{begin shipping_cost_entry.shipping_option.inspect rescue StandardError nil end}" end elsif has_dropship_items? && !order&.single_origin # here we want to use the cheapest option for one of the carriers on the supplier shipping_cost_entry ||= sorted_costs.select { |sc| origin_address.supported_carriers.present? ? origin_address.supported_carriers.include?(sc.shipping_option.carrier) : true }.first Rails.logger.debug do "set_proper_shipping_cost, dropship delivery, after fall back default to cheapest supplier carrier option: #{customer.default_shipping_option_name}, shipping_cost_entry.shipping_option: #{begin shipping_cost_entry.shipping_option.inspect rescue StandardError nil end}" end else # Fall back default is the customer's default/preferred shipping_cost_entry ||= sorted_costs.detect { |sc| sc.shipping_option.name == customer.default_shipping_option_name } Rails.logger.debug do "set_proper_shipping_cost, after fall back default to customer.default_shipping_option_name: #{customer.default_shipping_option_name}, shipping_cost_entry.shipping_option: #{begin shipping_cost_entry.shipping_option.inspect rescue StandardError nil end}" end end if ships_economy_package? Rails.logger.debug { "set_proper_shipping_cost, ships_economy_package?: #{ships_economy_package?}" } # use the override when shipping economy and reaching this point shipping_cost_entry ||= sorted_costs.detect { |sc| sc.is_override? } elsif ships_ltl_freight? # For LTL freight, always prefer cheapest non-override freight option non_override_freight = sorted_costs.reject { |sc| sc.is_override? }.sort_by { |sc| sc.cost.to_f } if shipping_cost_entry.nil? || shipping_cost_entry.is_override? shipping_cost_entry = non_override_freight.first if non_override_freight.any? end Rails.logger.debug { "set_proper_shipping_cost, ships_ltl_freight, selected: #{shipping_cost_entry&.shipping_option&.description}" } else Rails.logger.debug { "set_proper_shipping_cost, !ships_economy_package?: #{!ships_economy_package?}" } # Fall back default is the first non postal option shipping_cost_entry ||= sorted_costs.detect { |sc| !sc.shipping_option.is_postal? } end Rails.logger.debug do "set_proper_shipping_cost, after fall back default: shipping_cost_entry.shipping_option: #{begin shipping_cost_entry.shipping_option.inspect rescue StandardError nil end}" end # Store back the shipping option since it could be different or changed # IMPORTANT: Do NOT overwrite shipping_option if it's intentionally set to override # This preserves user's explicit override selection even when shipping costs are being refreshed current_override_intentional = self.shipping_option&.is_override? || order&.edi_shipping_option_name == 'override' if shipping_cost_entry && !current_override_intentional self.shipping_option = shipping_cost_entry.shipping_option elsif shipping_cost_entry && current_override_intentional && shipping_cost_entry.is_override? # Only update if the new entry is also override (to sync override cost with override option) self.shipping_option = shipping_cost_entry.shipping_option end Rails.logger.debug do "set_proper_shipping_cost, after all matches: shipping_option: #{begin shipping_option.inspect rescue StandardError nil end}" end return unless shipping_cost_entry apply_selected_shipping_cost!(shipping_cost_entry) true end |
#set_ship_labeled_at ⇒ Object
3240 3241 3242 |
# File 'app/models/delivery.rb', line 3240 def set_ship_labeled_at update_attribute(:ship_labeled_at, Time.current) if ship_labeled_at.blank? end |
#set_shipped_date ⇒ Object
3236 3237 3238 |
# File 'app/models/delivery.rb', line 3236 def set_shipped_date update_attribute(:shipped_date, Time.current) if shipped_date.blank? end |
#ship_ci_pdf ⇒ Object
3074 3075 3076 |
# File 'app/models/delivery.rb', line 3074 def ship_ci_pdf uploads.order(:id).reverse_order.find_by(category: 'ship_ci_pdf') end |
#ship_from_attributes ⇒ Object
3318 3319 3320 |
# File 'app/models/delivery.rb', line 3318 def ship_from_attributes order&.ship_from_attributes(self) || rma_for_return&.ship_from_attributes end |
#ship_labeled_via_heatwave? ⇒ Boolean
645 646 647 |
# File 'app/models/delivery.rb', line 645 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
649 650 651 |
# File 'app/models/delivery.rb', line 649 def ship_labeled_via_heatwave_or_manual_and_ship_insuring? ship_labeled_via_heatwave? || is_amazon_seller_central_veeqo? end |
#ship_natively_key ⇒ Object
3228 3229 3230 3231 3232 3233 3234 |
# File 'app/models/delivery.rb', line 3228 def ship_natively_key key = nil if chosen_shipping_method&.shipping_account_number && chosen_shipping_method.shipping_account_number.ship_natively? && chosen_shipping_method.shipping_account_number.account_number.present? key = chosen_shipping_method.shipping_account_number.account_number.to_sym end key end |
#ship_to_attributes ⇒ Object
3314 3315 3316 |
# File 'app/models/delivery.rb', line 3314 def ship_to_attributes order&.ship_to_attributes || rma_for_return&.ship_to_attributes end |
#shipment_contents_editable?(current_user = nil) ⇒ Boolean
2759 2760 2761 2762 2763 |
# File 'app/models/delivery.rb', line 2759 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 |
#shipments ⇒ ActiveRecord::Relation<Shipment>
115 |
# File 'app/models/delivery.rb', line 115 has_many :shipments, -> { order(:created_at) }, autosave: true, dependent: :destroy |
#shipments_for_packing ⇒ Object
782 783 784 |
# File 'app/models/delivery.rb', line 782 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
1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 |
# File 'app/models/delivery.rb', line 1670 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
2765 2766 2767 |
# File 'app/models/delivery.rb', line 2765 def shipments_voidable? SHIPPING_STATES.include?(state.to_sym) end |
#shipping? ⇒ Boolean
2769 2770 2771 |
# File 'app/models/delivery.rb', line 2769 def shipping? %i[pending_ship_confirm shipped].include?(state.to_sym) end |
#shipping_account_number ⇒ ShippingAccountNumber
98 |
# File 'app/models/delivery.rb', line 98 belongs_to :shipping_account_number, optional: true |
#shipping_costs ⇒ ActiveRecord::Relation<ShippingCost>
dependent destroy handled by trigger
110 |
# File 'app/models/delivery.rb', line 110 has_many :shipping_costs, -> { order(:cost) }, autosave: true |
#shipping_line_item ⇒ Object
1253 1254 1255 |
# File 'app/models/delivery.rb', line 1253 def shipping_line_item line_items.shipping_only.first end |
#shipping_method_friendly ⇒ Object
1009 1010 1011 1012 1013 1014 1015 1016 |
# File 'app/models/delivery.rb', line 1009 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) ⇒ Object
2106 2107 2108 2109 2110 2111 2112 2113 |
# File 'app/models/delivery.rb', line 2106 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.shipping_account_number ? " (using your linked account: #{sc.shipping_account_number.account_number})" : ''} #{verbose ? "(#{sc.shipping_option.delivery_commitment})" : ''} ", sc.shipping_option.id ] end end |
#shipping_option ⇒ ShippingOption
99 |
# File 'app/models/delivery.rb', line 99 belongs_to :shipping_option, optional: true |
#shipping_option_matches?(so_name) ⇒ Boolean
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 |
# File 'app/models/delivery.rb', line 1018 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
3820 3821 3822 |
# File 'app/models/delivery.rb', line 3820 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
3829 3830 3831 |
# File 'app/models/delivery.rb', line 3829 def ships_economy_ltl? ships_economy && ships_ltl_freight? end |
#ships_economy_package? ⇒ Boolean
3825 3826 3827 |
# File 'app/models/delivery.rb', line 3825 def ships_economy_package? ships_economy && !ships_ltl_freight? end |
#ships_ltl_freight? ⇒ Boolean
2094 2095 2096 |
# File 'app/models/delivery.rb', line 2094 def ships_ltl_freight? !is_warehouse_pickup? && (ltl_freight.present? || ltl_freight_guaranteed.present?) end |
#should_have_electronic_commercial_invoice? ⇒ Boolean
3082 3083 3084 |
# File 'app/models/delivery.rb', line 3082 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
653 654 655 656 |
# File 'app/models/delivery.rb', line 653 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
2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 |
# File 'app/models/delivery.rb', line 2705 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&..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
2098 2099 2100 |
# File 'app/models/delivery.rb', line 2098 def should_ship_ltl_freight? is_default_ltl_freight? || ltl_freight.present? || ltl_freight_guaranteed.present? end |
#show_packaging_on_pick_slip? ⇒ Boolean
1081 1082 1083 |
# File 'app/models/delivery.rb', line 1081 def show_packaging_on_pick_slip? destination_address&.is_amazon? end |
#simple_shipping_description_for_shipping_cost(sc = nil) ⇒ Object
1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 |
# File 'app/models/delivery.rb', line 1992 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? && (is_www? || 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
2515 2516 2517 |
# File 'app/models/delivery.rb', line 2515 def skip_destination_address_validation? instant_quote? || resource.try(:cart?) end |
#sorted_ground_shipping_costs(skip_override = false) ⇒ Object
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 |
# File 'app/models/delivery.rb', line 1766 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) ⇒ Object
1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 |
# File 'app/models/delivery.rb', line 1789 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{|sc| sc.id}.reverse.uniq{|sc| sc.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) ⇒ Object
1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 |
# File 'app/models/delivery.rb', line 1817 def sorted_shipping_costs_www_hash(sort_by_price: true) res = shipping_costs.skip_3rd_party.includes(:shipping_option) carriers = WWW_SHIPPING_CARRIERS[origin_address.country_iso3.to_sym] WWW_FREIGHT_CARRIERS[origin_address.country_iso3.to_sym] if destination_address.present? && destination_address.po_box? carriers = PO_BOX_CARRIERS end # If it's a PO box, then use postal carriers # ECONOMY SERVICE result_economy = shipping_costs.override_only # GROUND SERVICE # Use shipping_option.days_commitment for service level categorization (ground vs expedited vs rush) # NOT sc.days_commitment which now includes dynamic carrier estimates that vary by shipment gs = res.select { |sc| sc.shipping_option.days_commitment >= 3.5 || sc.shipping_option.carrier == 'SpeedeeDelivery' } result_ground = gs.select { |sp| carriers.include?(sp.shipping_option.carrier) && !sp.hide? }.sort_by { |a| [carriers.index(a.shipping_option.carrier) || 99, a.cost.to_f.round(2)] } result_ground = result_ground.sort_by { |a| a.cost.to_f.round(2) } if sort_by_price # EXPEDITED SERVICE es = res.select { |sc| sc.shipping_option.days_commitment >= 2 && sc.shipping_option.days_commitment < 3.5 && sc.shipping_option.carrier != 'SpeedeeDelivery' } result_expedited = es.select { |sp| carriers.include?(sp.shipping_option.carrier) && !sp.hide? }.sort_by { |a| [carriers.index(a.shipping_option.carrier) || 99, a.cost.to_f.round(2)] } result_expedited = result_expedited.sort_by { |a| a.cost.to_f.round(2) } if sort_by_price # RUSH SERVICE rs = res.select { |sc| sc.shipping_option.days_commitment < 2 && sc.shipping_option.carrier != 'SpeedeeDelivery' } result_rush = rs.select { |sp| carriers.include?(sp.shipping_option.carrier) && !sp.hide? }.sort_by { |a| [carriers.index(a.shipping_option.carrier) || 99, a.cost.to_f.round(2)] } result_rush = result_rush.sort_by { |a| a.cost.to_f.round(2) } if sort_by_price # FREIGHT SERVICE # filter these using our new is_freight column and choose the cheapest result_freight = [res.select { |sp| sp.shipping_option.is_freight && !sp.hide? }.min_by { |a| a.cost.to_f.round(2) }] { economy: result_economy.uniq, ground: result_ground.compact.uniq, faster: (result_expedited + result_rush).compact.uniq, freight: result_freight.compact.uniq } end |
#split_serial_numbers ⇒ Object
3558 3559 3560 |
# File 'app/models/delivery.rb', line 3558 def split_serial_numbers line_items.select(&:require_reservation?).each(&:split_serial_numbers) end |
#state_list ⇒ Object
3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 |
# File 'app/models/delivery.rb', line 3375 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 |
#store ⇒ Object
Alias for Resource#store
157 |
# File 'app/models/delivery.rb', line 157 delegate :store, to: :resource |
#supplier ⇒ Supplier
101 |
# File 'app/models/delivery.rb', line 101 belongs_to :supplier, optional: true |
#supported_shipping_carrier? ⇒ Boolean
3330 3331 3332 3333 3334 3335 3336 3337 3338 |
# File 'app/models/delivery.rb', line 3330 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 |
#tracking_link ⇒ Object
3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 |
# File 'app/models/delivery.rb', line 3174 def tracking_link resolved_carrier = case carrier when 'AmazonSeller' shipments.detect { |s| s.amz_carrier.present? }&.amz_carrier || carrier when 'WalmartSeller' shipments.detect { |s| s.sww_carrier.present? }&.sww_carrier || carrier else carrier end Shipment.tracking_link(resolved_carrier, master_tracking_number) end |
#uncommit_catalog_items ⇒ Object
3256 3257 3258 |
# File 'app/models/delivery.rb', line 3256 def uncommit_catalog_items Item::InventoryCommitter.crm_uncommit(line_items) end |
#uncommit_reserved_serial_numbers ⇒ Object
3260 3261 3262 |
# File 'app/models/delivery.rb', line 3260 def uncommit_reserved_serial_numbers line_items.each(&:uncommit_reserved_serial_numbers) end |
#unlink_serial_numbers_to_line_items ⇒ Object
3570 3571 3572 |
# File 'app/models/delivery.rb', line 3570 def unlink_serial_numbers_to_line_items line_items.each(&:unlink_serial_numbers) end |
#update_line_items_qty_shipped ⇒ Object
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).
3267 3268 3269 |
# File 'app/models/delivery.rb', line 3267 def update_line_items_qty_shipped line_items.update_all('qty_shipped = quantity') end |
#update_serial_numbers_shipped_count ⇒ Object
3578 3579 3580 |
# File 'app/models/delivery.rb', line 3578 def update_serial_numbers_shipped_count line_items.each(&:update_serial_numbers_shipped_count) end |
#uploads ⇒ ActiveRecord::Relation<Upload>
116 |
# File 'app/models/delivery.rb', line 116 has_many :uploads, as: :resource, dependent: :destroy |
#valid_for_generating_return_labels? ⇒ Boolean
3352 3353 3354 3355 3356 3357 |
# File 'app/models/delivery.rb', line 3352 def 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
3340 3341 3342 3343 3344 3345 3346 |
# File 'app/models/delivery.rb', line 3340 def 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
3348 3349 3350 |
# File 'app/models/delivery.rb', line 3348 def valid_for_voiding_ship_labels? pending_ship_confirm? && shipments.label_complete.any? && !is_part_of_manifest? end |
#validate_all_contents_allocated ⇒ Object
3594 3595 3596 3597 3598 |
# File 'app/models/delivery.rb', line 3594 def validate_all_contents_allocated return if all_lines_allocated_to_shipments? errors.add(:base, 'Not all lines are allocated properly to containers') end |
#versions_for_audit_trail(_params = {}) ⇒ Object
3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 |
# File 'app/models/delivery.rb', line 3647 def versions_for_audit_trail(_params = {}) query_sql = %q{ ( item_type = 'LineItem' AND reference_data @> '{"resource_type": "Order"}' AND reference_data @> '{"delivery_id": :id}' ) OR ( item_type = 'LineItem' AND reference_data @> '{"resource_type": "Quote"}' AND reference_data @> '{"delivery_id": :id}' ) OR (item_type = 'Delivery' and item_id = :id) } RecordVersion.where(query_sql, id:) end |
#void_early_label_on_order ⇒ Boolean
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)
3139 3140 3141 3142 3143 3144 3145 3146 3147 |
# File 'app/models/delivery.rb', line 3139 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_exists ⇒ Object
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.
3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 |
# File 'app/models/delivery.rb', line 3154 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_labels ⇒ Object
Void marketplace labels (Walmart SWW, Amazon, etc.) for all shipments with labels
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 |
# File 'app/models/delivery.rb', line 874 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.}") # Continue anyway - don't block the cancel operation end end end |
#void_shipments ⇒ Object
3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 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 |
# File 'app/models/delivery.rb', line 3090 def void_shipments # puts "!!void_shipments" if shipments.completed.any? && !is_part_of_manifest? shipments.manually_complete.each(&:manually_voided!) shipping_result = {} shipping_result[:status_code] = :ok if shipments.label_complete.any? tracking_numbers = shipments.label_complete.pluck(:tracking_number) shipping_result = WyShipping.void_delivery(self) # 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' end shipments.label_complete.each(&:label_voided!) # Clear carrier-assigned fields so the re-label form starts clean update_columns(master_tracking_number: nil, carrier_bol: nil, actual_shipping_cost: nil, shipengine_label_id: nil) reload.cancel_shipments! unless pending_ship_labels? end # Void the early-purchased label metadata on the order (if any) so the # picker no longer shows the early label banner and the order behaves # like a regular order going forward. Also resets purchase_label_early. void_early_label_on_order_if_exists { status_code: shipping_result[:status_code], status_message: "Please ensure you manually void manual shipments. Shipments voided. #{shipping_result[:status_message]}" } elsif void_early_label_on_order # No completed shipments yet, but there's an early label that hasn't been transferred - void it { status_code: :ok, status_message: 'Early-purchased shipping label has been voided.' } else { status_code: :error, status_message: "Can't void shipments because the delivery has no completed shipments OR delivery has been added to a manifest." } end end |