Class: Order
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Order
- Includes:
- AfterCommitEverywhere, Memery, Models::Auditable, Models::CouponDateOverridable, Models::InventoryCommittable, Models::Itemizable, Models::LegacyRateRequest, Models::Lineage, Models::LiquidMethods, Models::MultiRoom, Models::Notable, Models::Pickable, Models::Profitable, Models::RmaTransmittable, Models::ShipQuotable, Models::SourceAttributable, Models::TaxableResource, Models::ValidationErrorReporting, PgSearch::Model
- Defined in:
- app/models/order.rb
Overview
== Schema Information
Table name: orders
Database name: primary
id :integer not null, primary key
abandoned_cart_reminder :enum default("reminder_not_sent"), not null
actual_shipping_cost :decimal(10, 2)
allow_advance_credit_without_cc :boolean
amazon_checkout_session :string
attention_name_override :string(255)
bill_shipping_to_customer :boolean
billing_emails :string is an Array
bo_notification_sent_at :datetime
bulk_buy_request :boolean
cancellation_reason :string
cod_collection_type :string(255)
completion_date :date
credit_request_state :string
currency :string(255)
custom_order_agreement_status :integer default("custom_order_agreement_not_required"), not null
customer_reference :string(255)
disable_auto_coupon :boolean default(FALSE), not null
discount_adjustment_needed :boolean
do_not_reserve_stock :boolean default(FALSE), not null
early_label_metadata :jsonb
edi_auto_cancel_options :jsonb
edi_channel_order_support :string
edi_delayed_delivery_acknowledged_at :datetime
edi_is_pick_slip_required :boolean default(FALSE), not null
edi_orchestrator_partner :string
edi_order_date :date
edi_original_order_message :text
edi_original_ship_code :string
edi_po_number :string
edi_shipping_option_name :string
exclude_from_payment_check :boolean default(FALSE), not null
exported :boolean default(FALSE), not null
first_cart_view_date :datetime
first_review_request_processed_date :datetime
first_view_date :datetime
future_release_date :date
google_campaign_attribution :jsonb
google_conversion_meta :jsonb
hold_bo_release :boolean default(FALSE), not null
incorrectly_packaged_ups_canada_order :boolean
incorrectly_packaged_ups_canada_order_fixed :boolean
is_manual_hold :boolean default(FALSE), not null
jde_b_ab :integer
jde_s_ab :integer
label_instructions :string(255)
line_offset :decimal(10, 2)
line_total :decimal(10, 2)
ltl_freight :boolean
ltl_freight_guaranteed :boolean
manual_release_only :boolean
max_discount_override :decimal(4, 2)
min_profit_markup :integer default(0)
non_commissionable :boolean default(FALSE), not null
order_reception_type :string(255) default("CRM"), not null
order_type :string(255) default("SO")
override_coupon_date :date
override_coupon_date_without_limits :boolean default(FALSE), not null
override_line_lock :boolean default(FALSE), not null
packaged_items_md5_hash :string(255)
pickup :boolean
pinterest_conversion_meta :jsonb
precreate_rma :boolean default(FALSE), not null
price_match :decimal(10, 2)
pricing_program_description :string(255)
pricing_program_discount :integer
profit_total :decimal(10, 2)
purchase_label_early :boolean default(FALSE), not null
recalculate_discounts :boolean default(FALSE), not null
recalculate_shipping :boolean default(TRUE), not null
reference_number :string(255)
requested_deliver_by :date
requested_ship_before :date
requested_ship_on_or_after :date
retrieving_shipping_costs :boolean
return_label :boolean default(FALSE), not null
revenue_consolidated_at_time_of_checkout :decimal(10, 2)
review_request_processed :boolean default(FALSE), not null
review_snoozed_until :datetime
reviewed :boolean default(FALSE), not null
rma_reference :string(255)
sales_support_commission_date :date
saturday_delivery :boolean default(FALSE), not null
second_review_request_processed :boolean default(FALSE), not null
second_review_request_processed_date :datetime
shipment_instructions :text
shipment_reference_number :string
shipped_date :date
shipping_cost :decimal(10, 2)
shipping_cost_at_time_of_checkout :decimal(10, 2)
shipping_coupon :decimal(10, 2)
shipping_issue_alerted :boolean
shipping_method :string(255)
shipping_phone :string(255)
ships_economy :boolean default(FALSE), not null
signature_confirmation :boolean default(FALSE), not null
single_origin :boolean default(FALSE), not null
smartset_redemption_code :string(255)
sold_to_billing_address :integer
spiff_state :string(255)
state :string(255)
suggested_packaging_text :text
tax_date :date
tax_exempt :boolean default(FALSE), not null
tax_offset :decimal(10, 2)
tax_total :decimal(10, 2)
taxable_total :decimal(12, 2)
total :decimal(10, 2)
tracking_email :string(255) default([]), is an Array
transmission_email :string(255) default([]), is an Array
transmission_fax :string(255) default([]), is an Array
txid :uuid
uploads_count :integer default(0)
who_will_install :string
created_at :datetime
updated_at :datetime
buying_group_id :integer
contact_id :integer
creator_id :integer
customer_id :integer
default_credit_card_vault_id :integer
deleter_id :integer
edi_transaction_id :string
from_store_id :integer
local_sales_rep_id :integer
opportunity_id :integer
parent_id :integer
primary_sales_rep_id :integer
purchase_order_id :integer
quote_id :integer
resource_tax_rate_id :integer
rma_id :integer
sales_support_rep_id :integer
secondary_sales_rep_id :integer
shipping_account_number_id :integer
shipping_address_id :integer
source_id :integer
spiff_enrollment_id :integer
spiff_rep_id :integer
tax_exemption_id :integer
to_store_id :integer
updater_id :integer
visit_id :bigint
Indexes
idx_customer_id_order_type_created_at (customer_id,order_type,created_at)
idx_customer_id_order_type_state (customer_id,order_type,state)
idx_customer_id_review_snoozed_until (customer_id,review_snoozed_until)
idx_customer_id_state_shipped_date (customer_id,state,shipped_date)
idx_opportunity_id_state (opportunity_id,state)
idx_order_type_shipping_address_id (order_type,shipping_address_id)
idx_shipping_address_id_state (shipping_address_id,state)
index_orders_on_contact_id (contact_id) USING hash
index_orders_on_customer_id (customer_id) USING hash
index_orders_on_customer_reference (customer_reference) USING gin
index_orders_on_edi_po_number (edi_po_number) USING hash
index_orders_on_google_campaign_attribution (google_campaign_attribution) USING gin
index_orders_on_google_conversion_meta (google_conversion_meta) USING gin
index_orders_on_google_conversion_meta_result (((google_conversion_meta ->> 'result'::text)))
index_orders_on_opportunity_id (opportunity_id) USING hash
index_orders_on_packaged_items_md5_hash (packaged_items_md5_hash) USING hash
index_orders_on_parent_id (parent_id)
index_orders_on_purchase_order_id (purchase_order_id) USING hash
index_orders_on_quote_id (quote_id) USING hash
index_orders_on_reference_number (reference_number) UNIQUE
index_orders_on_rma_id (rma_id) USING hash
index_orders_on_sales_support_rep_id (sales_support_rep_id)
index_orders_on_shipped_date (shipped_date) USING brin
index_orders_on_shipping_address_id (shipping_address_id) USING hash
index_orders_on_smartset_redemption_code (smartset_redemption_code) WHERE (smartset_redemption_code IS NOT NULL) USING hash
index_orders_on_source_id (source_id) USING hash
index_orders_on_spiff_enrollment_id (spiff_enrollment_id) WHERE (spiff_enrollment_id IS NOT NULL) USING hash
index_orders_on_spiff_rep_id (spiff_rep_id) WHERE (spiff_rep_id IS NOT NULL) USING hash
index_orders_on_state (state) USING hash
index_orders_on_tax_exemption_id (tax_exemption_id) WHERE (tax_exemption_id IS NOT NULL) USING hash
index_orders_on_txid (txid) USING hash
index_orders_on_updated_at (updated_at) USING brin
index_orders_on_visit_id (visit_id) WHERE (visit_id IS NOT NULL) USING hash
orders_unique_edi_transaction_id_by_customer (customer_id,edi_transaction_id) UNIQUE
Foreign Keys
fk_rails_... (contact_id => parties.id) ON DELETE => nullify
fk_rails_... (parent_id => orders.id)
fk_rails_... (purchase_order_id => purchase_orders.id)
fk_rails_... (rma_id => rmas.id) ON DELETE => cascade
fk_rails_... (shipping_address_id => addresses.id)
fk_rails_... (source_id => sources.id)
fk_rails_... (visit_id => visits.id) ON DELETE => nullify
orders_tax_exemption_id_fk (tax_exemption_id => tax_exemptions.id)
Defined Under Namespace
Classes: BackOrderClientNotification, ContactLookup, CreateVcProcurementOrdersFromCsv, DefaultTrackingEmailExtractor, FollowUpScheduler, FraudDetector, Mover, SendAbandonedCartEmails, Splitter
Constant Summary collapse
- SALES_ORDER =
'SO'- MARKETING_ORDER =
'MO'- TECH_ORDER =
'TO'- CREDIT_ORDER =
'CO'- STORE_TRANSFER =
'ST'- SHIPPING_STATES =
%i[awaiting_deliveries processing_deliveries].freeze
- SOLD_STATES =
%i[awaiting_deliveries processing_deliveries partially_invoiced invoiced].freeze
- OPEN_FOR_CHANGE_STATES =
%i[cart in_shipping_estimate pending pending_payment pending_release_authorization in_cr_hold needs_serial_number_reservation cancelled fraudulent awaiting_completed_installation_plans].freeze
- OPEN_FOR_TAX_CHANGE_STATES =
%i[cart pending pending_payment pending_release_authorization in_cr_hold needs_serial_number_reservation crm_back_order awaiting_completed_installation_plans].freeze
- CAN_CR_HOLD_STATES =
%i[pending pending_payment awaiting_deliveries processing_deliveries profit_review crm_back_order].freeze
- RELEASABLE_STATES =
%i[cart in_cr_hold request_carrier crm_back_order pending_release_authorization profit_review pending_payment awaiting_deliveries processing_deliveries needs_serial_number_reservation awaiting_completed_installation_plans].freeze
- CANCELABLE_STATES =
%i[cart pending awaiting_return profit_review crm_back_order pending_release_authorization pending_payment in_cr_hold].freeze
- ROOM_PICKABLE_STATES =
%i[crm_back_order fraudulent cancelled pending pending_release_authorization pending_payment in_cr_hold].freeze
- CLOSED_STATES =
%i[shipped invoiced cancelled fraudulent].freeze
- LOCKED_STATES =
%i[cancelled awaiting_deliveries processing_deliveries shipped partially_invoiced invoiced fraudulent].freeze
- CAN_REQUEST_PRE_PACK_STATES =
%i[pending pending_payment profit_review in_cr_hold needs_serial_number_reservation].freeze
- RESTRICTED_ORDER_TYPES =
{ CREDIT_ORDER => 'Credit Order', STORE_TRANSFER => 'Store Transfer' }.freeze
- UNRESTRICTED_ORDER_TYPES =
{ SALES_ORDER => 'Sales Order', MARKETING_ORDER => 'Marketing Order', TECH_ORDER => 'Tech Order' }.freeze
- ALL_ORDER_TYPES =
RESTRICTED_ORDER_TYPES.merge(UNRESTRICTED_ORDER_TYPES)
- REFERENCE_NUMBER_PATTERN =
Regexp.new("^(#{ALL_ORDER_TYPES.keys.join('|')})(\\d+)$", Regexp::IGNORECASE)
- ALL_STATES =
Order.state_machines[:state].states.map(&:name).freeze
- NON_CART_STATES =
ALL_STATES - [:cart]
- EARLY_LABEL_RAPID_VOID_THRESHOLD_MINUTES =
Minimum time (in minutes) that must pass after early label purchase before auto-void is allowed
This prevents the race condition where label is voided before PDF is fully processed 3
Constants included from Models::InventoryCommittable
Models::InventoryCommittable::STATES_WITH_NO_EXPIRATION
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
-
#customer_id ⇒ Object
readonly
Customer can only have one cart per Contact.
-
#do_not_detect_shipping ⇒ Object
Returns the value of attribute do_not_detect_shipping.
-
#do_not_set_totals ⇒ Object
Returns the value of attribute do_not_set_totals.
-
#early_label_purchase_result ⇒ Object
Returns the value of attribute early_label_purchase_result.
- #from_store_id ⇒ Object readonly
-
#full_shipping_address_validation ⇒ Object
Returns the value of attribute full_shipping_address_validation.
-
#is_www ⇒ Object
Returns the value of attribute is_www.
-
#is_www_ship_by_zip ⇒ Object
Returns the value of attribute is_www_ship_by_zip.
- #max_discount_override ⇒ Object readonly
- #order_type ⇒ Object readonly
- #reference_number ⇒ Object readonly
-
#shipping_phone ⇒ Object
readonly
skip this validation for EDI orders, let it ride.
- #to_store_id ⇒ Object readonly
- #tracking_email ⇒ Object readonly
-
#update_shipping_address_with_contact ⇒ Object
Returns the value of attribute update_shipping_address_with_contact.
Attributes included from Models::Profitable
Attributes included from Models::Itemizable
#force_total_reset, #total_reset
Belongs to collapse
- #buying_group ⇒ BuyingGroup
- #contact ⇒ Contact
- #contact_point ⇒ ContactPoint
- #customer ⇒ Customer
- #default_credit_card_vault ⇒ CreditCardVault
- #from_store ⇒ Store
- #invoiced_local_sales_rep ⇒ Party
- #invoiced_primary_sales_rep ⇒ Party
- #invoiced_secondary_sales_rep ⇒ Party
- #opportunity ⇒ Opportunity
- #purchase_order ⇒ PurchaseOrder
- #quote ⇒ Quote
- #rma ⇒ Rma
- #sales_support_rep ⇒ Employee
-
#shipping_account_number ⇒ ShippingAccountNumber
DELIVERY REFACTOR HERE.
- #shipping_address ⇒ Address
- #sold_to_billing_address ⇒ Address
- #source ⇒ Source
- #spiff_enrollment ⇒ SpiffEnrollment
- #spiff_rep ⇒ Contact
- #to_store ⇒ Store
- #visit ⇒ Visit
Methods included from Models::Auditable
Methods included from Models::TaxableResource
Methods included from Models::Itemizable
Has one collapse
Has many collapse
- #activities ⇒ ActiveRecord::Relation<Activity>
- #communications ⇒ ActiveRecord::Relation<Communication>
- #credit_memos ⇒ ActiveRecord::Relation<CreditMemo>
- #deliveries ⇒ ActiveRecord::Relation<Delivery>
- #delivery_activities ⇒ ActiveRecord::Relation<Activity>
- #direct_shipments ⇒ ActiveRecord::Relation<Shipment>
- #drop_ship_purchase_orders ⇒ ActiveRecord::Relation<DropShipPurchaseOrder>
- #edi_communication_logs ⇒ ActiveRecord::Relation<EdiCommunicationLog>
- #edi_documents ⇒ ActiveRecord::Relation<EdiDocument>
- #invoices ⇒ ActiveRecord::Relation<Invoice>
- #linked_support_cases ⇒ ActiveRecord::Relation<LinkedSupportCase>
- #material_alerts ⇒ ActiveRecord::Relation<MaterialAlert>
- #messaging_logs ⇒ ActiveRecord::Relation<MessagingLog>
- #payments ⇒ ActiveRecord::Relation<Payment>
- #preset_jobs ⇒ ActiveRecord::Relation<PresetJob>
- #rma_items ⇒ ActiveRecord::Relation<RmaItem>
- #rmas ⇒ ActiveRecord::Relation<Rma>
- #shipments ⇒ ActiveRecord::Relation<Shipment>
- #uploads ⇒ ActiveRecord::Relation<Upload>
- #vouchers ⇒ ActiveRecord::Relation<Voucher>
Methods included from Models::Pickable
Methods included from Models::Itemizable
Has and belongs to many collapse
Methods included from Models::MultiRoom
Delegated Instance Attributes collapse
-
#billing_address ⇒ Object
Alias for Customer#billing_address.
-
#billing_entity ⇒ Object
Alias for Customer#billing_entity.
-
#catalog ⇒ Object
Alias for Customer#catalog.
-
#company ⇒ Object
Alias for Customer#company.
-
#include_po_prefix? ⇒ Object
Alias for Customer#include_po_prefix?.
-
#is_amazon_com? ⇒ Object
Alias for Customer#is_amazon_com?.
-
#is_amazon_seller_central? ⇒ Object
Alias for Customer#is_amazon_seller_central?.
-
#is_canadian_tire? ⇒ Object
Alias for Customer#is_canadian_tire?.
-
#is_costco_ca? ⇒ Object
Alias for Customer#is_costco_ca?.
-
#is_home_depot_can? ⇒ Object
Alias for Customer#is_home_depot_can?.
-
#is_home_depot_usa? ⇒ Object
Alias for Customer#is_home_depot_usa?.
-
#is_houzz? ⇒ Object
Alias for Customer#is_houzz?.
-
#is_part_of_lowes_ca? ⇒ Object
Alias for Customer#is_part_of_lowes_ca?.
-
#is_part_of_lowes_com? ⇒ Object
Alias for Customer#is_part_of_lowes_com?.
-
#is_walmart_ca? ⇒ Object
Alias for Customer#is_walmart_ca?.
-
#is_wasn4_or_wat0f? ⇒ Object
Alias for Customer#is_wasn4_or_wat0f?.
-
#store_id ⇒ Object
Alias for Store#id.
Class Method Summary collapse
-
.active ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are active.
-
.active_spiffs ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are active spiffs.
-
.all_awaiting_deliveries ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are all awaiting deliveries.
- .all_order_types_for_select ⇒ Object
-
.awaiting_completed_installation_plans ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are awaiting completed installation plans.
-
.awaiting_payment_spiff ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are awaiting payment spiff.
-
.back_order ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are back order.
-
.by_company_id ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by company id.
-
.by_primary_rep_id ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by primary rep id.
-
.by_report_grouping ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by report grouping.
-
.by_report_grouping_all_when_nil ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by report grouping all when nil.
-
.by_sales_rep_id ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by sales rep id.
-
.by_store ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by store.
-
.by_store_id ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by store id.
-
.cancelled ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are cancelled.
-
.carts ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are carts.
-
.co_only ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are co only.
-
.contains_coupon_ids ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are contains coupon ids.
-
.contains_item_ids ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are contains item ids.
-
.contains_service_items ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are contains service items.
-
.correctly_packaged ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are correctly packaged.
- .custom_order_agreement_statuses_for_select ⇒ Object
-
.customer_reference_search ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are customer reference search.
-
.draft_spiff ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are draft spiff.
-
.edi_orders ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are edi orders.
-
.future_release ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are future release.
-
.has_manual_preset_form ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are has manual preset form.
-
.held ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are held.
-
.held_sales_orders ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are held sales orders.
-
.in_progress ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are in progress.
-
.in_state ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are in state.
-
.incorrectly_packaged_ups_canada_order ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are incorrectly packaged ups canada order.
-
.invoiced ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are invoiced.
-
.like_lookup ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are like lookup.
-
.limit_to_fba ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are limit to fba.
-
.locked ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are locked.
-
.lookup ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are lookup.
-
.mo_only ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are mo only.
-
.most_recent_first ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are most recent first.
-
.non_carts ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are non carts.
-
.non_credit ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are non credit.
-
.not_cancelled ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not cancelled.
-
.not_in_pre_pack ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not in pre pack.
-
.not_open_for_change ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not open for change.
-
.not_partially_invoiced ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not partially invoiced.
-
.not_processing_deliveries ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not processing deliveries.
-
.not_sold ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not sold.
-
.open_for_change ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are open for change.
-
.open_for_tax_update ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are open for tax update.
- .order_count(company_id = nil, where_conditions = nil, where_not_conditions = nil) ⇒ Object
- .order_type_from_opportunity(opportunity) ⇒ Object
- .order_type_from_quote(quote) ⇒ Object
-
.paid_spiff ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are paid spiff.
-
.pending ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are pending.
-
.pending_payment ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are pending payment.
-
.pending_payment_and_unpaid_invoices ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are pending payment and unpaid invoices.
- .po_number_barcode(po_number:, file_path: nil) ⇒ Object
-
.positive_value ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are positive value.
-
.profit_review ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are profit review.
-
.quick_stats_shipping ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are quick stats shipping.
-
.quick_stats_sold ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are quick stats sold.
- .reception_type_for_select ⇒ Object
-
.returnable_types ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are returnable types.
-
.room_not_pickable ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are room not pickable.
-
.sales_orders ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are sales orders.
- .selectable_order_types ⇒ Object
-
.so_only ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are so only.
-
.sold ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are sold.
-
.st_only ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are st only.
- .states_for_select ⇒ Object
-
.with_amazon_payments ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with amazon payments.
-
.with_associations ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with associations.
-
.with_line_items ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with line items.
-
.with_payments ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with payments.
-
.with_review ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with review.
-
.without_review ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are without review.
Instance Method Summary collapse
- #accounting_hold_order? ⇒ Boolean
- #add_customer_to_campaign ⇒ Object
- #add_item(sku, qty) ⇒ Object
-
#add_multiple_items(sku_array = []) ⇒ Object
sku array being ['UDG4-4999', qty: 1, 'SS-01', qty: 2].
- #adjusted_actual_shipping_cost ⇒ Object
- #all_deliveries_cancelable?(current_user = nil) ⇒ Boolean
- #all_deliveries_invoiced? ⇒ Boolean
- #all_funds_available?(ignore_cod = false) ⇒ Boolean
- #all_funds_not_available_and_shippable? ⇒ Boolean protected
- #all_items_in_stock ⇒ Object
- #all_participant_ids ⇒ Object
- #all_payments_are_valid? ⇒ Boolean
- #all_rooms_not_orderable? ⇒ Boolean
- #all_support_cases ⇒ Object
- #all_uploads ⇒ Object
- #allows_edi_split? ⇒ Boolean
- #already_has_smartinstall_request? ⇒ Boolean
-
#amazon_buy_shipping_eligible? ⇒ Boolean
Check if order is eligible for Amazon Buy Shipping early label purchase.
-
#amzbs_packing_slip_included? ⇒ Boolean
Amazon Buy Shipping label PDFs include a packing slip page, so no separate upload is needed when an AMZBS shipping option is selected.
- #any_rooms_not_orderable? ⇒ Boolean
- #applies_for_smartinstall ⇒ Object
-
#apply_tier2_pricing? ⇒ Boolean
Whether or not to apply the tier2 pricing (customer discount) by default.
- #attention_name ⇒ Object
- #attention_name=(value) ⇒ Object
- #auto_reserve_serial_numbers ⇒ Object
- #awaiting_future_deliveries? ⇒ Boolean
- #balance(ignore_cod = false) ⇒ Object
- #belongs_to_smartservice_group? ⇒ Boolean
- #build_activity ⇒ Object
-
#build_early_label_carrier_info(label_result, delivery = nil) ⇒ Hash
Build carrier info hash for early label EDI confirm (Walmart only).
-
#build_early_label_tracking_url(carrier, tracking_number) ⇒ String?
Build tracking URL for early label.
- #calculate_actual_shipping_cost ⇒ Object
- #can_auto_reserve_serial_numbers? ⇒ Boolean
- #can_be_cancelled? ⇒ Boolean
- #can_be_returned? ⇒ Boolean
- #can_cr_hold?(current_user = nil) ⇒ Boolean
- #can_edit_future_release_date? ⇒ Boolean
- #cancel_deliveries_pre_pack ⇒ Object
- #cancel_or_destroy ⇒ Object
- #cancelable? ⇒ Boolean
- #cannot_delete_reason(account = nil) ⇒ Object
- #cart_identifier ⇒ Object
- #cart_or_in_shipping_estimate? ⇒ Boolean
- #check_payments_status ⇒ Object
- #check_sales_rep ⇒ Object protected
- #ci_invoice_credit_order? ⇒ Boolean
- #clear_shipped_date ⇒ Object
- #closed_state? ⇒ Boolean
-
#company_review_url ⇒ Object
Reviews.io dynamic link for company review using the order reference as order_id and the customer CN number as customer_identifier (for CRM context).
-
#company_review_url_for_confirmation(email: nil) ⇒ Object
Reviews.io dynamic link for the order confirmation / thank-you page.
- #complete? ⇒ Boolean
- #completed_regular_deliveries ⇒ Object
- #completely_shipped? ⇒ Boolean
- #consolidate_revenue ⇒ Object
-
#contact_combo ⇒ Object
Used by dynamic contact lookup on order creation allowing interaction with the tom-select input on account_managers.html.erb Contact can either be existing (single integer value for contact_id) or new contact.
-
#contact_combo=(val) ⇒ Object
Used by dynamic contact lookup on order creation allowing interaction with the tom-select input on account_managers.html.erb Contact can either be existing (single integer value for contact_id) or new contact if val is in format Customer|customer_id|full_name.
- #contact_combo_for_select ⇒ Object
- #copy_customer_reps ⇒ Object
- #copy_invoice_reps ⇒ Object
-
#copy_items_from(order) ⇒ Boolean
Copy non-shipping line items from another order (typically a guest cart) into this one, then destroy the source if it is itself a cart.
- #copy_shipping_reference_number_to_deliveries ⇒ Object
- #country ⇒ Object
- #create_credit_memo ⇒ Object
- #create_smartfix_ticket ⇒ Object
- #create_smartguide_ticket ⇒ Object
- #create_smartinstall_ticket ⇒ Object
- #crm_link ⇒ Object
- #currency_symbol ⇒ Object
-
#custom_shipping_labels ⇒ Object
Pulls all custom ship labels from the order, deliveries and shipments.
- #customer_qualifies_for_free_online_shipping? ⇒ Boolean
- #deep_dup ⇒ Object
- #default_billing_emails ⇒ Object
- #deferred_payments_captured_and_ready_for_shipping? ⇒ Boolean
- #deletable? ⇒ Boolean
- #doesnt_already_has_smartinstall? ⇒ Boolean
-
#download_and_store_early_label_pdf_atomic(shipper, label_result, is_amazon: false) ⇒ Upload?
SAFEGUARD A: Atomic PDF download and storage Downloads and stores the label PDF with retries, ensuring it's persisted before returning.
-
#early_label_failure_notification(error_message) ⇒ Object
Send EDI admin notification when early label purchase fails This ensures failures are not silent and the team is notified.
-
#early_label_flash_message ⇒ Hash
Generate flash messages based on early label purchase result Called by controllers after order transitions to awaiting_deliveries.
-
#early_label_purchase_enabled_for_partner? ⇒ Boolean
Checks whether the partner's orchestrator has early label purchase enabled.
-
#early_label_purchased_recently? ⇒ Boolean
Check if early label was purchased recently (within the rapid void threshold) Used by SAFEGUARD B to prevent race conditions.
-
#early_label_shipments_match?(delivery) ⇒ Hash
Check if current shipments match the early label shipments Used to detect if warehouse staff changed the packing.
-
#early_label_upload ⇒ Upload?
Get the early label upload (PDF) if it exists.
- #echecks_requiring_authorization ⇒ Object
- #edi_cancellation_reason_description ⇒ Object
- #edi_cancellation_reasons ⇒ Object
- #edi_force_price_match ⇒ Object
- #edi_orchestrator ⇒ Object
- #edi_price_matcheable? ⇒ Boolean
- #editing_locked? ⇒ Boolean
- #effective_date_for_coupon ⇒ Object
- #email_for_order_confirmation ⇒ Object
- #email_options_for_tracking_email ⇒ Object
- #empty? ⇒ Boolean
- #encrypted_id ⇒ Object
- #errors_with_deliveries_errors ⇒ Object
- #estimate_next_available_date_from_out_of_stock_items ⇒ Object
- #find_gbraid ⇒ Object
- #find_gclid ⇒ Object
- #find_wbraid ⇒ Object
-
#fire_early_label_edi_confirm(delivery, label_result) ⇒ Object
Fire EDI ship confirm with early label tracking info.
-
#fire_early_label_edi_confirm_amazon(delivery, label_result) ⇒ Object
Amazon early label ship confirm — mirrors Edi::Amazon::ConfirmMessageProcessor#acknowledge_order but builds the message from order data since no shipment record exists yet.
-
#fire_early_label_edi_confirm_walmart(delivery, label_result) ⇒ Object
Walmart early label ship confirm — original Walmart-specific flow.
- #first_po_number ⇒ Object
- #first_tracking_email ⇒ Object
- #fix_future_release_date ⇒ Object
- #formatted_po_number ⇒ Object
- #fully_funded_by_advance_replacement? ⇒ Boolean
- #funded_by_advance_replacement? ⇒ Boolean
- #funded_by_cod? ⇒ Boolean
- #funds_available_and_ready_for_warehouse? ⇒ Boolean protected
- #generate_spiff_training_activity ⇒ Object
- #get_expected_ship_date_time ⇒ Object
- #get_scheduled_ship_date_time ⇒ Object
- #has_authorized_payment? ⇒ Boolean
- #has_committed_serial_number_reservations? ⇒ Boolean
- #has_custom_packing_slip? ⇒ Boolean
-
#has_early_purchased_label? ⇒ Boolean
Check if order has an active (non-voided) early-purchased label.
- #has_incomplete_reservations? ⇒ Boolean
- #has_selected_heated_items? ⇒ Boolean
- #has_shipping_method? ⇒ Boolean
- #has_unreserved_line_items? ⇒ Boolean
- #has_web_rooms_needing_installation_plans? ⇒ Boolean
-
#hold_for_early_label_mismatch!(delivery, reason) ⇒ Object
Puts the order on CR hold with a descriptive note when early label purchase cannot proceed because the selected shipping rate doesn't match available marketplace rates.
- #hold_order_reasons ⇒ Object
- #hold_orders? ⇒ Boolean
- #human_state_name ⇒ Object
- #includes_schedulable_service? ⇒ Boolean
- #inherited_attention_name ⇒ Object
- #installation_country_iso ⇒ Object
- #installation_country_iso3 ⇒ Object
- #installation_is_within_range? ⇒ Boolean
- #installation_postal_code ⇒ Object
- #installation_state_code ⇒ Object
- #invoice_balance ⇒ Object
- #is_crm? ⇒ Boolean
- #is_edi_order? ⇒ Boolean
- #is_fba? ⇒ Boolean
- #is_from_myprojects? ⇒ Boolean
- #is_marketing_order? ⇒ Boolean
- #is_online? ⇒ Boolean
-
#is_price_editable? ⇒ Boolean
Tells whether or not the order can have its line item discounted and msrp price editable by the price editable concerns, this method is called by the ability check first.
- #is_regular_order? ⇒ Boolean
- #is_remote_service? ⇒ Boolean
- #is_rma_replacement? ⇒ Boolean
- #is_rma_return? ⇒ Boolean
- #is_sales_order? ⇒ Boolean
- #is_smartfit_service? ⇒ Boolean
- #is_smartfix_service? ⇒ Boolean
- #is_smartguide_service? ⇒ Boolean
- #is_smartinstall_service? ⇒ Boolean
- #is_store_transfer? ⇒ Boolean
- #is_subject_to_minimum_qty_rules? ⇒ Boolean
- #is_tech_order? ⇒ Boolean
- #is_warehouse_pickup? ⇒ Boolean
- #job_name ⇒ Object
- #local_sales_rep ⇒ Object
-
#log_early_label_event(event_type, message, details = {}) ⇒ Object
Log an event to the early label API log Used for debugging and visibility into what happened during early label purchase.
- #mark_serial_coupons_as_used ⇒ Object
-
#marketplace_early_label_eligible? ⇒ Boolean
Check if order is eligible for marketplace early label purchase (Walmart SWW or Amazon Buy Shipping).
-
#merge_shipper_api_log(shipper) ⇒ Object
Merge raw HTTP API call logs from the shipper into the order's API log.
- #minimum_order_quantity_violations ⇒ Object
- #move_deliveries_from_quoting! ⇒ Object
- #move_deliveries_to_quoting! ⇒ Object
- #move_service_case_from_pending_service_payment ⇒ Object
- #name ⇒ Object
- #need_to_recalculate_shipping ⇒ Object
- #needs_attention_issues ⇒ Object
- #needs_spiff_training? ⇒ Boolean
- #new_customer_online_order ⇒ Object
- #new_customer_order ⇒ Object
- #new_ss_ticket(service, activity_type) ⇒ Object
- #next_six_months_with_end_dates ⇒ Object
- #notify_pre_pack_cancellation ⇒ Object
- #ok_to_delete?(account = nil) ⇒ Boolean
- #online_order? ⇒ Boolean
- #open_activities_counter ⇒ Object
- #opportunities ⇒ Object
- #opportunity_name ⇒ Object
- #opportunity_name=(val) ⇒ Object
- #order_closed? ⇒ Boolean
- #order_emails ⇒ Object
- #order_type_title ⇒ Object
- #partially_shipped? ⇒ Boolean
- #participants_options_for_select ⇒ Object
- #party_for_order_confirmation ⇒ Object
- #pay_on_spiff? ⇒ Boolean
- #payment_method ⇒ Object
- #payment_method_requires_authorization? ⇒ Boolean
- #payment_options(source) ⇒ Object
- #payments_requiring_authorization ⇒ Object
- #payments_with_fraud_report ⇒ Object
- #paypal_invoices_paid? ⇒ Boolean
- #po_number(include_po_prefix: false, limit: nil) ⇒ Object
- #po_number_barcode(file_path: nil) ⇒ Object
- #po_numbers ⇒ Object
- #potential_fraud? ⇒ Boolean
- #potential_fraud_reasons ⇒ Object
- #precreated_rma_credit_available ⇒ Object
- #prevent_recalculate_shipping? ⇒ Boolean
- #primary_party ⇒ Object
- #primary_rep_name ⇒ Object
- #primary_sales_rep ⇒ Object
- #product_review_url ⇒ Object
- #prune_cart_rooms ⇒ Object
- #public_path ⇒ Object
- #public_payment_link ⇒ Object
-
#purchase_early_label_if_requested ⇒ Hash?
Purchase shipping label early if requested and order is eligible Called from after_transition to awaiting_deliveries.
- #quotes ⇒ Object
- #ready_for_pending_payment? ⇒ Boolean
- #ready_for_service? ⇒ Boolean
- #ready_for_shipping? ⇒ Boolean
- #ready_for_warehouse? ⇒ Boolean
- #recipient_name ⇒ Object
- #related_activities ⇒ Object
- #related_activities_ids ⇒ Object
- #release_order_or_hold ⇒ Object
- #remove_room_configuration(rc) ⇒ Object
- #require_cc_for_advance_credit? ⇒ Boolean
- #requires_intervention? ⇒ Boolean
- #reset_cart ⇒ Object
- #restricted_order_type? ⇒ Boolean
-
#reviewer ⇒ Object
Returns the reviewer info for Reviews.io invitation Prefers contact over customer if present.
- #rma_cancel ⇒ Object
-
#save_early_label_api_log ⇒ Object
Save the accumulated API log to the order's early_label_metadata Called at the end of purchase_early_label_if_requested (success or failure).
- #schedule_follow_up_activity ⇒ Object
- #search_text ⇒ Object protected
- #secondary_sales_rep ⇒ Object
-
#selected_shipping_cost_supports_early_label?(selected_sc, delivery) ⇒ Boolean
Checks whether the delivery's selected shipping cost is compatible with the marketplace early-label flow.
- #selection_name ⇒ Object
- #send_back_order_notification ⇒ Object
-
#send_early_label_void_alert(reason:, details:) ⇒ Object
Send alert email when early label void fails or is blocked SAFEGUARD C: Ensures operations team is notified of label issues.
- #send_online_order_confirmation ⇒ Object
- #send_payment_automatically_authorized_notification ⇒ Object
- #send_profit_review_notification ⇒ Object
- #send_ready_for_pickup_email ⇒ Object
- #send_release_authorization_notification ⇒ Object
- #send_request_carrier_assignment_notification ⇒ Object
- #send_tracking_email ⇒ Object
- #send_tracking_template_email(template_code) ⇒ Object
- #service_only_order? ⇒ Boolean
- #set_currency ⇒ Object protected
-
#set_custom_order_agreement ⇒ Object
Checks for the presence of custom products in excess of $2k.
- #set_default_tracking_email(force: false) ⇒ Object
- #set_min_profit_markup ⇒ Object
- #set_opportunity_source ⇒ Object
- #set_shipped_date ⇒ Object
- #ship_from_attributes(delivery = nil) ⇒ Object
- #ship_to_attributes ⇒ Object
- #ship_weight ⇒ Object
- #shipping_address_valid_and_not_po_box_if_present(rate_shopping = false) ⇒ Object
- #shipping_cutoff_advance_order? ⇒ Boolean
- #shipping_cutoff_next_day? ⇒ Boolean
- #shipping_cutoff_same_day? ⇒ Boolean
-
#shipping_date_warnings ⇒ Object
Warnings about shipping dates that don't block order release These are displayed to users but don't prevent state transitions Note: requested_ship_before is automatically advanced when the user revisits the shipping form, so no warning is needed when it's in the past.
- #should_commit_stock? ⇒ Boolean
- #smartservice_ticket ⇒ Object
- #sms_enabled_numbers ⇒ Object
- #sms_messages ⇒ Object
- #spiff_reward ⇒ Object
- #state_description(describe_state = nil) ⇒ Object
- #state_list ⇒ Object
- #stock_status ⇒ Object
- #stop_for_pre_pack? ⇒ Boolean
- #stop_for_profit_review? ⇒ Boolean
- #store ⇒ Object
- #store_additional_early_label_pdfs(additional_labels) ⇒ Object
-
#store_amazon_label_pdf_atomic(label_result, tracking_number, marketplace) ⇒ Object
Amazon: label data is returned inline — store it and verify persistence.
-
#store_early_label_pdf(label_data, tracking_number) ⇒ Upload?
Store early label PDF on the order.
-
#store_mock_early_label_pdf(tracking_number, _carrier) ⇒ Object
Store a mock label PDF for development/testing Uses a pre-generated mock PDF since the sandbox doesn't return real label PDFs.
-
#store_walmart_label_pdf_atomic(shipper, _label_result, tracking_number, carrier, marketplace) ⇒ Object
Walmart: download label via API with retries, then store and verify persistence.
- #suggested_shipments ⇒ Object
- #support_case_ref ⇒ Object
- #support_case_ref=(support_case_ref) ⇒ Object
-
#suppress_edi_duplicate_warning? ⇒ Boolean
Suppresses duplicate order warnings for off-book delivery arrangements.
- #sync_opportunity ⇒ Object
- #terms_available? ⇒ Boolean protected
- #to_label ⇒ Object
- #to_liquid ⇒ Object
- #to_s ⇒ Object
- #total_cod ⇒ Object
- #total_money ⇒ Object
- #total_payments_authorized ⇒ Object
- #track_profit? ⇒ Boolean
- #tracking_email_address ⇒ Object
- #uncommit_undelivered_line_items ⇒ Object
- #unfulfilled_dropship_items ⇒ Object
- #update_customer_status ⇒ Object protected
- #update_linked_po_if_st ⇒ Object
- #update_sales_support_rep(new_sales_support_rep_id, new_commission_date = nil) ⇒ Object
- #versions_for_audit_trail(_params = {}) ⇒ Object
- #void_credit_rma_item ⇒ Object
-
#void_early_label!(reason: 'Manual void requested', reset_flag: true) ⇒ Hash
Void the early label with a custom reason (public method for external callers) Also resets purchase_label_early flag so the next ship-label goes through normal flow.
-
#void_early_label_if_exists ⇒ Hash?
Void early-purchased label when order is pulled back from awaiting_deliveries Called from before_transition to cancelled/fraudulent/in_cr_hold.
-
#void_early_label_upload ⇒ Object
Re-categorize the early label PDF as voided so it no longer appears as an active attachment but is kept for audit trail (mirrors the ship_label_pdf -> voided_ship_label_pdf pattern in Shipment).
- #void_payments(report_fraud = false) ⇒ Object
-
#walmart_sww_eligible? ⇒ Boolean
Check if order is eligible for Walmart Ship with Walmart early label purchase.
Methods included from Models::SourceAttributable
#has_google_ads_attribution?, #source_locked?, #source_locked_reason, #visit_with_google_click_id?
Methods included from Models::Lineage
#ancestors, #ancestors_ids, #children_and_roots, #descendants, #descendants_ids, #ensure_non_recursive_lineage, #family_members, #generate_full_name, #generate_full_name_array, #lineage, #lineage_array, #lineage_simple, #root, #root_id, #self_ancestors_and_descendants, #self_ancestors_and_descendants_ids, #self_and_ancestors, #self_and_ancestors_ids, #self_and_children, #self_and_descendants, #self_and_descendants_ids, #self_and_siblings, #self_and_siblings_ids, #siblings, #siblings_ids
Methods included from Models::InventoryCommittable
#can_be_committed?, #can_be_uncommitted?, #commit_line_items, #determine_commit_expiration_date, #has_committed_line_items?, #uncommit_line_items
Methods included from Models::CouponDateOverridable
Methods included from Models::LegacyRateRequest
#last_shipping_rate_request_result, #last_shipping_rate_request_result=
Methods included from Models::RmaTransmittable
#rma_available_email_addresses, #rma_available_fax_numbers
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, #validate_min_profit_markup?
Methods included from Models::Auditable
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
Methods included from Models::MultiRoom
#add_line_items_to_all_rooms, #add_lines_for_room_configuration, #all_rooms_complete?, #all_rooms_complete_or_cancelled?, #all_rooms_complete_or_cancelled_or_draft?, #all_rooms_in_design?, #all_rooms_ppd?, #any_room_ppd?, #any_rooms_in_design?, #get_operating_costs, #get_recommended_materials, #heated_sq_ft, #insulation_sq_ft, #prioritize_room, #remove_line_items_from_all_rooms, #remove_lines_for_room_configuration, #replace_line_items_in_all_rooms, #suggested_items, #suggested_services, #synchronization_targets
Methods included from Models::ShipQuotable
#carrier, #chosen_shipping_method, #days_commitment, #determine_origin_address, #everything_in_stock?, #friendly_shipping_method, #is_drop_ship?, #is_override?, #line_items_grouped_by_deliveries_quoting, #line_items_match_deliveries_if_any, #need_to_pre_pack_reasons, #one_time_shipping_address, #one_time_shipping_address=, #qualifies_for_cod?, #refresh_deliveries_quoting, #reset_deliveries_shipping_option, #reset_shipping, #retrieve_friendly_shipping_method, #retrieve_shipping_costs, #ship_quoted, #shipping_non_db_default_but_db_customer?, #shipping_signature_confirmation_non_db_default_but_db_customer?, #shipping_via_wy_but_has_shipping_account?, #ships_economy_package?, #ships_freight?, #ships_freight_but_address_not_freight_ready?, #should_ship_freight?, #should_ship_freight_but_address_not_freight_ready?, #validate_deliveries
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::Pickable
#all_my_publications, #append_suggested_items, #append_suggested_materials, #control_capacity, #determine_catalog_for_picking, #determine_skus_to_filter, #discounted_shipping_total, #electrical_heating_elements, #fix_catalog, #get_material_alerts, #has_custom_products?, #invalidate_material_alerts!, #line_items_grouped_by_room_configuration, #meets_custom_products_threshold?, #pricing_program_discount_factor, #purge_empty_line_items, #soft_recalc, #subtotal, #subtotal_after_trade_discount_without_shipping, #subtotal_discounted_without_shipping, #subtotal_msrp_without_shipping, #synchronize_lines, #total_amps_by_product_line, #total_coverage_by_product_line, #total_linear_feet_by_product_line, #total_spec_by_product_line, #total_watts_by_product_line, #underlayments
Methods included from Models::TaxableResource
#apply_tax_rate_to_line_items, #build_tax_params, #calculate_tax_for_all_lines, #copy_tax_rate, #effective_date, #get_rates_for_line, #get_tax_rate, #manual_rate_goods, #manual_rate_services, #manual_rate_shipping, #origin_address, #refresh_tax_rate, #resource_not_taxable?, #set_initial_tax_rate, #should_refresh_tax_rate?, #state_code, #state_code_sym, #taxes_grouped_by_rate, #taxes_grouped_by_type
Methods included from Models::Itemizable
#add_line_item, #additional_items, #assign_sequence, #breakdown_of_prices, #calculate_actual_insured_value, #calculate_discounts, #calculate_shipping_cost, #coupon_search, #customer_applied_coupons, #customer_can_apply_coupon?, #discounts_changed?, #discounts_grouped_by_coupon, #discounts_subtotal, #effective_discount, #effective_shipping_discount, #has_kits?, #has_kits_or_serial_numbers?, #has_serial_numbers?, #is_credit_order?, #line_items_requiring_serial_number, #line_items_with_counters, #line_total_plus_tax, #main_rep, #perform_db_total, #purge_empty_quoting_deliveries, #purge_shipping_when_no_other_lines, #remove_line_item, #require_total_reset?, #reset_discount, #set_for_recalc, #set_signature_confirmation_on_shipping_address_change, #set_totals, #shipping_conditions_changed?, #shipping_discounted, #shipping_method_changed?, #should_recalculate_shipping?, #smartinstall_data, #smartsupport_data, #subtotal_cogs, #sync_shipping_line, #total_cogs
Methods included from Models::Notable
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Models::EventPublishable
Instance Attribute Details
#customer_id ⇒ Object (readonly)
Customer can only have one cart per Contact
Validations (if => #cart? ):
- Uniqueness ({ scope: %i[state contact_id], message: 'Only one cart is allowed per user' })
317 |
# File 'app/models/order.rb', line 317 validates :customer_id, uniqueness: { scope: %i[state contact_id], message: 'Only one cart is allowed per user' }, if: :cart? |
#do_not_detect_shipping ⇒ Object
Returns the value of attribute do_not_detect_shipping.
336 337 338 |
# File 'app/models/order.rb', line 336 def do_not_detect_shipping @do_not_detect_shipping end |
#do_not_set_totals ⇒ Object
Returns the value of attribute do_not_set_totals.
336 337 338 |
# File 'app/models/order.rb', line 336 def do_not_set_totals @do_not_set_totals end |
#early_label_purchase_result ⇒ Object
Returns the value of attribute early_label_purchase_result.
336 337 338 |
# File 'app/models/order.rb', line 336 def early_label_purchase_result @early_label_purchase_result end |
#from_store_id ⇒ Object (readonly)
321 |
# File 'app/models/order.rb', line 321 validates :from_store_id, :to_store_id, presence: { on: :create, if: proc { |o| o.order_type == STORE_TRANSFER } } |
#full_shipping_address_validation ⇒ Object
Returns the value of attribute full_shipping_address_validation.
336 337 338 |
# File 'app/models/order.rb', line 336 def full_shipping_address_validation @full_shipping_address_validation end |
#is_www ⇒ Object
Returns the value of attribute is_www.
336 337 338 |
# File 'app/models/order.rb', line 336 def is_www @is_www end |
#is_www_ship_by_zip ⇒ Object
Returns the value of attribute is_www_ship_by_zip.
336 337 338 |
# File 'app/models/order.rb', line 336 def is_www_ship_by_zip @is_www_ship_by_zip end |
#max_discount_override ⇒ Object (readonly)
324 |
# File 'app/models/order.rb', line 324 validates :max_discount_override, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100, allow_nil: true } |
#order_type ⇒ Object (readonly)
318 |
# File 'app/models/order.rb', line 318 validates :order_type, presence: true |
#reference_number ⇒ Object (readonly)
320 |
# File 'app/models/order.rb', line 320 validates :reference_number, presence: { unless: :cart_or_in_shipping_estimate? } |
#shipping_phone ⇒ Object (readonly)
skip this validation for EDI orders, let it ride
Validations (unless => proc { |o| o.is_edi_order? } ):
- Phone_format
322 |
# File 'app/models/order.rb', line 322 validates :shipping_phone, phone_format: true, unless: proc { |o| o.is_edi_order? } |
#to_store_id ⇒ Object (readonly)
321 |
# File 'app/models/order.rb', line 321 validates :from_store_id, :to_store_id, presence: { on: :create, if: proc { |o| o.order_type == STORE_TRANSFER } } |
#tracking_email ⇒ Object (readonly)
323 |
# File 'app/models/order.rb', line 323 validates :tracking_email, email_format: true |
#update_shipping_address_with_contact ⇒ Object
Returns the value of attribute update_shipping_address_with_contact.
336 337 338 |
# File 'app/models/order.rb', line 336 def update_shipping_address_with_contact @update_shipping_address_with_contact end |
Class Method Details
.active ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are active. Active Record Scope
422 |
# File 'app/models/order.rb', line 422 scope :active, -> { where.not(state: %w[pending cancelled fraudulent cart in_shipping_estimate]) } |
.active_spiffs ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are active spiffs. Active Record Scope
453 |
# File 'app/models/order.rb', line 453 scope :active_spiffs, -> { where(spiff_state: %w[awaiting_payment paid]) } |
.all_awaiting_deliveries ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are all awaiting deliveries. Active Record Scope
407 |
# File 'app/models/order.rb', line 407 scope :all_awaiting_deliveries, -> { where(state: SHIPPING_STATES) } |
.all_order_types_for_select ⇒ Object
1344 1345 1346 |
# File 'app/models/order.rb', line 1344 def self.all_order_types_for_select ALL_ORDER_TYPES.map { |code, desc| [desc, code] } end |
.awaiting_completed_installation_plans ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are awaiting completed installation plans. Active Record Scope
451 |
# File 'app/models/order.rb', line 451 scope :awaiting_completed_installation_plans, -> { where(state: :awaiting_completed_installation_plans) } |
.awaiting_payment_spiff ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are awaiting payment spiff. Active Record Scope
450 |
# File 'app/models/order.rb', line 450 scope :awaiting_payment_spiff, -> { where(spiff_state: 'awaiting_payment') } |
.back_order ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are back order. Active Record Scope
440 |
# File 'app/models/order.rb', line 440 scope :back_order, -> { where(state: :crm_back_order) } |
.by_company_id ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by company id. Active Record Scope
412 |
# File 'app/models/order.rb', line 412 scope :by_company_id, ->(company_id) { joins(customer: { catalog: :store }).where(stores: { company_id: }) } |
.by_primary_rep_id ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by primary rep id. Active Record Scope
413 |
# File 'app/models/order.rb', line 413 scope :by_primary_rep_id, ->(rep_id) { joins(:customer).where('parties.primary_sales_rep_id = ?', rep_id) } |
.by_report_grouping ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by report grouping. Active Record Scope
466 |
# File 'app/models/order.rb', line 466 scope :by_report_grouping, ->(report_grouping) { joins(:customer).where('parties.report_grouping = ?', report_grouping) } |
.by_report_grouping_all_when_nil ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by report grouping all when nil. Active Record Scope
465 |
# File 'app/models/order.rb', line 465 scope :by_report_grouping_all_when_nil, ->(report_grouping) { report_grouping.present? ? joins(:customer).where('parties.report_grouping = ?', report_grouping) : joins(:customer).where('1=1') } |
.by_sales_rep_id ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by sales rep id. Active Record Scope
468 |
# File 'app/models/order.rb', line 468 scope :by_sales_rep_id, ->(sales_rep_id) { joins(:customer).where('(parties.primary_sales_rep_id = :sales_rep_id) OR (parties.secondary_sales_rep_id = :sales_rep_id) OR (parties.local_sales_rep_id = :sales_rep_id)', sales_rep_id:) } |
.by_store ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by store. Active Record Scope
418 |
# File 'app/models/order.rb', line 418 scope :by_store, ->(store) { where(currency: store.currency) } |
.by_store_id ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are by store id. Active Record Scope
411 |
# File 'app/models/order.rb', line 411 scope :by_store_id, ->(store_id) { joins(customer: :catalog).where(catalogs: { store_id: }) } |
.cancelled ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are cancelled. Active Record Scope
421 |
# File 'app/models/order.rb', line 421 scope :cancelled, -> { where(state: 'cancelled') } |
.carts ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are carts. Active Record Scope
428 |
# File 'app/models/order.rb', line 428 scope :carts, -> { where(state: :cart) } |
.co_only ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are co only. Active Record Scope
443 |
# File 'app/models/order.rb', line 443 scope :co_only, -> { where(order_type: CREDIT_ORDER) } |
.contains_coupon_ids ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are contains coupon ids. Active Record Scope
459 |
# File 'app/models/order.rb', line 459 scope :contains_coupon_ids, ->(coupon_ids) { where("EXISTS (select discounts.id from discounts where discounts.coupon_id IN (?) and discounts.itemizable_type = 'Order' and discounts.itemizable_id = orders.id)", coupon_ids) } |
.contains_item_ids ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are contains item ids. Active Record Scope
456 457 458 |
# File 'app/models/order.rb', line 456 scope :contains_item_ids, ->(item_ids) { where("EXISTS (select li.id from line_items li inner join catalog_items ci on ci.id= li.catalog_item_id inner join store_items si on si.id = ci.store_item_id where li.resource_id = orders.id and li.resource_type = 'Order' and si.item_id IN (?))", item_ids) } |
.contains_service_items ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are contains service items. Active Record Scope
460 |
# File 'app/models/order.rb', line 460 scope :contains_service_items, -> { contains_item_ids(Item.services.ids) } |
.correctly_packaged ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are correctly packaged. Active Record Scope
431 |
# File 'app/models/order.rb', line 431 scope :correctly_packaged, -> { where('orders.incorrectly_package_ups_canada_order IS NOT TRUE OR (orders.incorrectly_package_ups_canada_order IS TRUE and orders.incorrectly_packaged_ups_canada_order_fixed IS TRUE)') } |
.custom_order_agreement_statuses_for_select ⇒ Object
1336 1337 1338 |
# File 'app/models/order.rb', line 1336 def self.custom_order_agreement_statuses_for_select Order.custom_order_agreement_statuses.keys.map { |e| [e.humanize, e] } end |
.customer_reference_search ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are customer reference search. Active Record Scope
473 |
# File 'app/models/order.rb', line 473 scope :customer_reference_search, ->(q) { where(Order[:customer_reference].matches("%#{q}%")).order([Arel.sql('orders.customer_reference <-> ?'), q]) } |
.draft_spiff ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are draft spiff. Active Record Scope
449 |
# File 'app/models/order.rb', line 449 scope :draft_spiff, -> { where("spiff_enrollment_id is not null and spiff_state = 'draft'") } |
.edi_orders ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are edi orders. Active Record Scope
472 |
# File 'app/models/order.rb', line 472 scope :edi_orders, -> { where.not(orders: { edi_transaction_id: nil }) } |
.future_release ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are future release. Active Record Scope
467 |
# File 'app/models/order.rb', line 467 scope :future_release, -> { where("(orders.future_release_date IS NOT NULL) AND orders.order_type <> 'CO' AND (orders.state NOT IN(#{Order::CLOSED_STATES.map { |s| "'#{s}'" }.join(',')}))") } |
.has_manual_preset_form ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are has manual preset form. Active Record Scope
461 |
# File 'app/models/order.rb', line 461 scope :has_manual_preset_form, -> { where("EXISTS(select 1 from uploads where uploads.resource_type = 'Order' and uploads.resource_id = orders.id and uploads.category = 'manual_smart_preset_form')") } |
.held ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are held. Active Record Scope
439 |
# File 'app/models/order.rb', line 439 scope :held, -> { where(state: :in_cr_hold) } |
.held_sales_orders ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are held sales orders. Active Record Scope
455 |
# File 'app/models/order.rb', line 455 scope :held_sales_orders, -> { sales_orders.where(state: %w[pending pending_payment pending_release_authorization in_cr_hold]) } |
.in_progress ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are in progress. Active Record Scope
432 |
# File 'app/models/order.rb', line 432 scope :in_progress, -> { so_only.where.not(state: %w[cart invoiced cancelled fraudulent]) } |
.in_state ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are in state. Active Record Scope
410 |
# File 'app/models/order.rb', line 410 scope :in_state, ->(state) { where(state:) } |
.incorrectly_packaged_ups_canada_order ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are incorrectly packaged ups canada order. Active Record Scope
430 |
# File 'app/models/order.rb', line 430 scope :incorrectly_packaged_ups_canada_order, -> { where('orders.incorrectly_package_ups_canada_order IS TRUE and orders.incorrectly_packaged_ups_canada_order_fixed IS NOT TRUE') } |
.invoiced ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are invoiced. Active Record Scope
425 |
# File 'app/models/order.rb', line 425 scope :invoiced, -> { where(state: 'invoiced') } |
.like_lookup ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are like lookup. Active Record Scope
463 |
# File 'app/models/order.rb', line 463 scope :like_lookup, ->(q) { where('orders.reference_number like :term', { term: "%#{q}%" }) } |
.limit_to_fba ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are limit to fba. Active Record Scope
464 |
# File 'app/models/order.rb', line 464 scope :limit_to_fba, -> { joins(customer: :billing_address).where('parties.id = :amz_id OR addresses.party_id = :amz_id', amz_id: CustomerConstants::AMAZON_COM_ID) } |
.locked ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are locked. Active Record Scope
469 |
# File 'app/models/order.rb', line 469 scope :locked, -> { where(state: LOCKED_STATES) } |
.lookup ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are lookup. Active Record Scope
462 |
# File 'app/models/order.rb', line 462 scope :lookup, ->(q) { where('orders.reference_number = :term', { term: q }) } |
.mo_only ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are mo only. Active Record Scope
444 |
# File 'app/models/order.rb', line 444 scope :mo_only, -> { where(order_type: MARKETING_ORDER) } |
.most_recent_first ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are most recent first. Active Record Scope
438 |
# File 'app/models/order.rb', line 438 scope :most_recent_first, -> { order('orders.created_at DESC') } |
.non_carts ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are non carts. Active Record Scope
429 |
# File 'app/models/order.rb', line 429 scope :non_carts, -> { where(state: NON_CART_STATES) } |
.non_credit ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are non credit. Active Record Scope
427 |
# File 'app/models/order.rb', line 427 scope :non_credit, -> { where.not(order_type: CREDIT_ORDER) } |
.not_cancelled ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not cancelled. Active Record Scope
423 |
# File 'app/models/order.rb', line 423 scope :not_cancelled, -> { where.not(state: 'cancelled') } |
.not_in_pre_pack ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not in pre pack. Active Record Scope
433 |
# File 'app/models/order.rb', line 433 scope :not_in_pre_pack, -> { so_only.where.not(state: %w[pre_pack]) } |
.not_open_for_change ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not open for change. Active Record Scope
416 |
# File 'app/models/order.rb', line 416 scope :not_open_for_change, -> { where.not(state: OPEN_FOR_CHANGE_STATES) } |
.not_partially_invoiced ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not partially invoiced. Active Record Scope
426 |
# File 'app/models/order.rb', line 426 scope :not_partially_invoiced, -> { where.not(state: %w[invoiced partially_invoiced]) } |
.not_processing_deliveries ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not processing deliveries. Active Record Scope
424 |
# File 'app/models/order.rb', line 424 scope :not_processing_deliveries, -> { where.not(state: 'processing_deliveries') } |
.not_sold ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are not sold. Active Record Scope
409 |
# File 'app/models/order.rb', line 409 scope :not_sold, -> { where.not(state: SOLD_STATES) } |
.open_for_change ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are open for change. Active Record Scope
414 |
# File 'app/models/order.rb', line 414 scope :open_for_change, -> { where(state: OPEN_FOR_CHANGE_STATES) } |
.open_for_tax_update ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are open for tax update. Active Record Scope
415 |
# File 'app/models/order.rb', line 415 scope :open_for_tax_update, -> { where(state: OPEN_FOR_TAX_CHANGE_STATES) } |
.order_count(company_id = nil, where_conditions = nil, where_not_conditions = nil) ⇒ Object
1348 1349 1350 1351 1352 1353 1354 |
# File 'app/models/order.rb', line 1348 def self.order_count(company_id = nil, where_conditions = nil, where_not_conditions = nil) o = Order.joins(customer: { catalog: :store }).order('orders.id') o = o.by_company_id(company_id) unless company_id.nil? o = o.where(where_conditions) unless where_conditions.nil? o = o.where.not(where_not_conditions) unless where_not_conditions.nil? o.count end |
.order_type_from_opportunity(opportunity) ⇒ Object
4627 4628 4629 |
# File 'app/models/order.rb', line 4627 def self.order_type_from_opportunity(opportunity) "#{opportunity.opportunity_type}O" end |
.order_type_from_quote(quote) ⇒ Object
4623 4624 4625 |
# File 'app/models/order.rb', line 4623 def self.order_type_from_quote(quote) { TQ: 'TO', MQ: 'MO', SQ: 'SO' }[quote.quote_type.to_sym] || 'SO' end |
.paid_spiff ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are paid spiff. Active Record Scope
452 |
# File 'app/models/order.rb', line 452 scope :paid_spiff, -> { where(spiff_state: 'paid') } |
.pending ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are pending. Active Record Scope
434 |
# File 'app/models/order.rb', line 434 scope :pending, -> { where(state: 'pending') } |
.pending_payment ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are pending payment. Active Record Scope
436 |
# File 'app/models/order.rb', line 436 scope :pending_payment, -> { where(state: 'pending_payment') } |
.pending_payment_and_unpaid_invoices ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are pending payment and unpaid invoices. Active Record Scope
437 |
# File 'app/models/order.rb', line 437 scope :pending_payment_and_unpaid_invoices, -> { Order.where(id: (joins(:invoices).where("invoices.state = 'unpaid'") + pending_payment).pluck(:id)) } |
.po_number_barcode(po_number:, file_path: nil) ⇒ Object
2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 |
# File 'app/models/order.rb', line 2298 def self.(po_number:, file_path: nil) return unless po_number.present? # we just want ASCII here, no spaces or weird cut and paste characters, see https://github.com/toretore/barby/issues/61 po_number = po_number.to_s.scan(/\S/).join.encode(Encoding::ASCII_8BIT, invalid: :replace, undef: :replace, replace: '') require 'barby' require 'barby/barcode/code_128' require 'barby/outputter/png_outputter' = Barby::Code128B.new(po_number) png = .to_png if file_path File.open(file_path, 'wb') do |file| file.write(png) file.flush file.fsync end file_path else png end end |
.positive_value ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are positive value. Active Record Scope
446 |
# File 'app/models/order.rb', line 446 scope :positive_value, -> { where('line_total > 0') } |
.profit_review ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are profit review. Active Record Scope
435 |
# File 'app/models/order.rb', line 435 scope :profit_review, -> { where(state: 'profit_review') } |
.quick_stats_shipping ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are quick stats shipping. Active Record Scope
448 |
# File 'app/models/order.rb', line 448 scope :quick_stats_shipping, -> { so_only.positive_value.in_state(SHIPPING_STATES).select('sum(line_total) as sum_line_total, currency').group(:currency) } |
.quick_stats_sold ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are quick stats sold. Active Record Scope
447 |
# File 'app/models/order.rb', line 447 scope :quick_stats_sold, -> { so_only.positive_value.in_state(SOLD_STATES).select('sum(line_total) as sum_line_total, currency').group(:currency) } |
.reception_type_for_select ⇒ Object
2607 2608 2609 2610 2611 2612 2613 2614 |
# File 'app/models/order.rb', line 2607 def self.reception_type_for_select unless @reception_types @reception_types = {} @reception_types['Online'] = 'Online' @reception_types['CRM'] = 'CRM' end @reception_types end |
.returnable_types ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are returnable types. Active Record Scope
445 |
# File 'app/models/order.rb', line 445 scope :returnable_types, -> { where(order_type: [SALES_ORDER, MARKETING_ORDER, TECH_ORDER]) } |
.room_not_pickable ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are room not pickable. Active Record Scope
417 |
# File 'app/models/order.rb', line 417 scope :room_not_pickable, -> { where.not(state: ROOM_PICKABLE_STATES) } |
.sales_orders ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are sales orders. Active Record Scope
454 |
# File 'app/models/order.rb', line 454 scope :sales_orders, -> { non_carts.so_only.active } |
.selectable_order_types ⇒ Object
1340 1341 1342 |
# File 'app/models/order.rb', line 1340 def self.selectable_order_types UNRESTRICTED_ORDER_TYPES.map { |code, desc| [desc, code] } end |
.so_only ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are so only. Active Record Scope
442 |
# File 'app/models/order.rb', line 442 scope :so_only, -> { where(order_type: SALES_ORDER) } |
.sold ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are sold. Active Record Scope
408 |
# File 'app/models/order.rb', line 408 scope :sold, -> { where(state: SOLD_STATES) } |
.st_only ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are st only. Active Record Scope
441 |
# File 'app/models/order.rb', line 441 scope :st_only, -> { where(order_type: STORE_TRANSFER) } |
.states_for_select ⇒ Object
1360 1361 1362 |
# File 'app/models/order.rb', line 1360 def self.states_for_select state_machine.states.sort_by(&:human_name).map { |s| [s.human_name, s.value] } end |
.with_amazon_payments ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with amazon payments. Active Record Scope
475 |
# File 'app/models/order.rb', line 475 scope :with_amazon_payments, -> { joins(:payments).where(payments: { category: Payment::AMAZON_PAY }) } |
.with_associations ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with associations. Active Record Scope
419 |
# File 'app/models/order.rb', line 419 scope :with_associations, -> { includes(:shipments, :shipping_account_number, :shipping_address, :creator, { customer: [:buying_group, { catalog: :store }] }) } |
.with_line_items ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with line items. Active Record Scope
420 |
# File 'app/models/order.rb', line 420 scope :with_line_items, -> { includes(line_items: { catalog_item: { store_item: :item } }) } |
.with_payments ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with payments. Active Record Scope
474 |
# File 'app/models/order.rb', line 474 scope :with_payments, -> { joins(:payments) } |
.with_review ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are with review. Active Record Scope
470 |
# File 'app/models/order.rb', line 470 scope :with_review, -> { where(reviewed: true) } |
.without_review ⇒ ActiveRecord::Relation<Order>
A relation of Orders that are without review. Active Record Scope
471 |
# File 'app/models/order.rb', line 471 scope :without_review, -> { where(reviewed: false) } |
Instance Method Details
#accounting_hold_order? ⇒ Boolean
1725 1726 1727 |
# File 'app/models/order.rb', line 1725 def accounting_hold_order? customer.on_hold || || potential_fraud? end |
#activities ⇒ ActiveRecord::Relation<Activity>
274 |
# File 'app/models/order.rb', line 274 has_many :activities, as: :resource, dependent: :nullify |
#add_customer_to_campaign ⇒ Object
1364 1365 1366 1367 1368 1369 1370 |
# File 'app/models/order.rb', line 1364 def add_customer_to_campaign return unless saved_change_to_source_id? return unless source return unless source.linked_to_campaign? source.add_customer_to_campaign(customer) end |
#add_item(sku, qty) ⇒ Object
2206 2207 2208 2209 2210 2211 |
# File 'app/models/order.rb', line 2206 def add_item(sku, qty) return if editing_locked? sku_array = [{ sku:, qty: }] add_multiple_items(sku_array) end |
#add_multiple_items(sku_array = []) ⇒ Object
sku array being ['UDG4-4999', qty: 1, 'SS-01', qty: 2]
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 |
# File 'app/models/order.rb', line 2174 def add_multiple_items(sku_array = []) return if editing_locked? return if line_items.size > 100 # Then we suspect it's a bot adding things to the cart added_items = [] sku_array.each do |hsh| sku = hsh[:sku] qty = hsh[:qty] || 1 hsh[:room_configuration_id] ci = catalog.catalog_items.public_catalog_items.by_skus(sku).first raise Order::ItemNotFound.new("#{sku} not found") unless ci.present? li = add_line_item(catalog_item_id: ci.id, quantity: qty, room_configuration_id: nil, do_not_autosave: true) added_items << { id: li.id, sku: li.sku, name: li.name, category: li.reported_category_name, quantity: li.quantity } end if added_items.present? self.recalculate_shipping = true # doesn't hurt to set it self.recalculate_discounts = true # ensure tier2 and auto-apply discounts are calculated self.force_total_reset = true begin save! rescue ActiveRecord::RecordNotUnique => e raise unless e..include?('index_discounts_unique_per_itemizable') discounts.reload save! end end added_items end |
#adjusted_actual_shipping_cost ⇒ Object
4516 4517 4518 |
# File 'app/models/order.rb', line 4516 def adjusted_actual_shipping_cost deliveries.invoiced.to_a.sum { |d| d.adjusted_actual_shipping_cost.to_f } end |
#all_deliveries_cancelable?(current_user = nil) ⇒ Boolean
1019 1020 1021 |
# File 'app/models/order.rb', line 1019 def all_deliveries_cancelable?(current_user = nil) deliveries.active.empty? || deliveries.active.all? { |d| d.cancelable?(current_user) } end |
#all_deliveries_invoiced? ⇒ Boolean
1978 1979 1980 |
# File 'app/models/order.rb', line 1978 def all_deliveries_invoiced? deliveries.active.present? && deliveries.active.all?(&:invoiced?) end |
#all_funds_available?(ignore_cod = false) ⇒ Boolean
1974 1975 1976 |
# File 'app/models/order.rb', line 1974 def all_funds_available?(ignore_cod = false) balance(ignore_cod) <= 0 end |
#all_funds_not_available_and_shippable? ⇒ Boolean (protected)
4601 4602 4603 |
# File 'app/models/order.rb', line 4601 def all_funds_not_available_and_shippable? !all_funds_available?(true) && shipping_address && chosen_shipping_method.present? end |
#all_items_in_stock ⇒ Object
1992 1993 1994 |
# File 'app/models/order.rb', line 1992 def all_items_in_stock stock_status == :ok end |
#all_participant_ids ⇒ Object
1124 1125 1126 1127 1128 1129 1130 |
# File 'app/models/order.rb', line 1124 def all_participant_ids party_ids = [] party_ids += opportunity.all_participants.pluck(:id) if opportunity party_ids << customer_id party_ids << contact_id party_ids.compact.uniq end |
#all_payments_are_valid? ⇒ Boolean
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 |
# File 'app/models/order.rb', line 1023 def all_payments_are_valid? return true unless payments.bread_payments.any? if payments.bread_payments.where(state: 'authorized').sum(:amount) >= total true else errors.add :base, 'Financed order cannot be modified to have a higher total than the existing payment. Please create a new order for the extra items or extra cost.' false end end |
#all_rooms_not_orderable? ⇒ Boolean
1693 1694 1695 |
# File 'app/models/order.rb', line 1693 def all_rooms_not_orderable? room_configurations.any? && room_configurations.all? { |rc| !rc.orderable? } end |
#all_support_cases ⇒ Object
1420 1421 1422 1423 1424 |
# File 'app/models/order.rb', line 1420 def all_support_cases support_case_ids linked_support_cases.pluck(:id) SupportCase.where(id: support_case_ids).order('support_cases.case_number desc') end |
#all_uploads ⇒ Object
4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 |
# File 'app/models/order.rb', line 4540 def all_uploads ret_uploads = if delivery_ids.present? shipment_ids = Shipment.where(delivery_id: delivery_ids).pluck(:id) conditions = ["(resource_type = 'Order' and resource_id = :order_id)", "(resource_type = 'Delivery' and resource_id IN (:delivery_ids))"] conditions << "(resource_type = 'Shipment' and resource_id IN (:shipment_ids))" if shipment_ids.any? Upload.where(conditions.join(' OR '), order_id: id, delivery_ids:, shipment_ids:) else uploads end ret_uploads.includes(:resource).order(Upload[:created_at].desc) end |
#allows_edi_split? ⇒ Boolean
4281 4282 4283 |
# File 'app/models/order.rb', line 4281 def allows_edi_split? edi_channel_order_support.present? && edi_channel_order_support.in?(%w[SPLIT_ORDERS SPLIT_ORDER_LINES]) end |
#already_has_smartinstall_request? ⇒ Boolean
2336 2337 2338 |
# File 'app/models/order.rb', line 2336 def already_has_smartinstall_request? activities.where(activity_type_id: ActivityTypeConstants::LEAD_SSI).any? end |
#amazon_buy_shipping_eligible? ⇒ Boolean
Check if order is eligible for Amazon Buy Shipping early label purchase
3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 |
# File 'app/models/order.rb', line 3508 def amazon_buy_shipping_eligible? return false unless is_edi_order? return false unless edi_orchestrator_partner&.start_with?('amazon_seller') begin orchestrator = Edi::Amazon::Orchestrator.new(edi_orchestrator_partner.to_sym) orchestrator&.buy_shipping_enabled? rescue StandardError => e Rails.logger.warn("[EarlyLabel] Error checking Amazon Buy Shipping eligibility: #{e.}") false end end |
#amzbs_packing_slip_included? ⇒ Boolean
Amazon Buy Shipping label PDFs include a packing slip page, so no
separate upload is needed when an AMZBS shipping option is selected.
1859 1860 1861 |
# File 'app/models/order.rb', line 1859 def amzbs_packing_slip_included? deliveries.any? { |d| d.shipping_option&.carrier == 'AmazonSeller' } end |
#any_rooms_not_orderable? ⇒ Boolean
1687 1688 1689 1690 1691 |
# File 'app/models/order.rb', line 1687 def any_rooms_not_orderable? # (self.order_reception_type == "Online" or online_order?) and self.room_configurations.any?{|rc| (rc.room_layout_attached? and !rc.complete?)} # here we are now allowing direct order of online rooms via HW so relax the test for any orders with uncompleted rooms that have layouts room_configurations.any? { |rc| !rc.orderable? } end |
#applies_for_smartinstall ⇒ Object
2325 2326 2327 2328 2329 2330 |
# File 'app/models/order.rb', line 2325 def applies_for_smartinstall return false # added by Roman Aug 19th until service refactor return true if installation_is_within_range? and customer.is_homeowner? and has_selected_heated_items? and doesnt_already_has_smartinstall? false end |
#apply_tier2_pricing? ⇒ Boolean
Whether or not to apply the tier2 pricing (customer discount) by default
2412 2413 2414 |
# File 'app/models/order.rb', line 2412 def apply_tier2_pricing? is_sales_order? end |
#attention_name ⇒ Object
2583 2584 2585 |
# File 'app/models/order.rb', line 2583 def attention_name attention_name_override || inherited_attention_name end |
#attention_name=(value) ⇒ Object
2587 2588 2589 2590 2591 |
# File 'app/models/order.rb', line 2587 def attention_name=(value) return if inherited_attention_name && value =~ /inherited_attention_name/i self.attention_name_override = value end |
#auto_reserve_serial_numbers ⇒ Object
1326 1327 1328 1329 1330 |
# File 'app/models/order.rb', line 1326 def auto_reserve_serial_numbers return unless can_auto_reserve_serial_numbers? line_items.select(&:require_reservation?).each(&:auto_reserve_serial_numbers) end |
#awaiting_future_deliveries? ⇒ Boolean
4308 4309 4310 |
# File 'app/models/order.rb', line 4308 def awaiting_future_deliveries? awaiting_deliveries? && deliveries.for_future_release.present? end |
#balance(ignore_cod = false) ⇒ Object
1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 |
# File 'app/models/order.rb', line 1926 def balance(ignore_cod = false) return BigDecimal('0.0') if is_store_transfer? || (!ignore_cod && funded_by_cod?) total = deliveries.sum(:total) || BigDecimal('0.0') bal = total deliveries.each do |dq| dq_total = dq.total || BigDecimal('0.0') dq_auth = dq.(currency) || BigDecimal('0.0') bal -= if dq_auth >= dq_total dq_total else dq_auth end end bal < 0 ? BigDecimal('0.0') : bal end |
#belongs_to_smartservice_group? ⇒ Boolean
2025 2026 2027 |
# File 'app/models/order.rb', line 2025 def belongs_to_smartservice_group? is_smartfit_service? or is_smartinstall_service? or is_smartguide_service? or is_smartfix_service? end |
#billing_address ⇒ Object
Alias for Customer#billing_address
296 |
# File 'app/models/order.rb', line 296 delegate :catalog, :billing_address, :billing_entity, :company, to: :customer |
#billing_entity ⇒ Object
Alias for Customer#billing_entity
296 |
# File 'app/models/order.rb', line 296 delegate :catalog, :billing_address, :billing_entity, :company, to: :customer |
#build_activity ⇒ Object
1416 1417 1418 |
# File 'app/models/order.rb', line 1416 def build_activity activities.build resource: self, party: primary_party end |
#build_early_label_carrier_info(label_result, delivery = nil) ⇒ Hash
Build carrier info hash for early label EDI confirm (Walmart only)
Delegates to ShipCodeMapper for carrier name + methodCode derivation,
matching the behavior of the normal (non-early) ship confirm flow
in ConfirmMessageProcessor#build_order_line_status -> ShipCodeMapper#carrier_info.
4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 |
# File 'app/models/order.rb', line 4240 def build_early_label_carrier_info(label_result, delivery = nil) actual_carrier = label_result[:carrier] || 'OTHER' orchestrator = Edi::Walmart::Orchestrator.new(edi_orchestrator_partner.to_sym) mapper = orchestrator.ship_code_mapper carrier_code = mapper.carrier_code(actual_carrier) carrier_name = if carrier_code { carrier: carrier_code } else { otherCarrier: actual_carrier.to_s } end description = delivery&.selected_shipping_cost&.description method = mapper.extract_method_from_delivery(description, carrier_code || actual_carrier) { carrierName: carrier_name, methodCode: method } end |
#build_early_label_tracking_url(carrier, tracking_number) ⇒ String?
Build tracking URL for early label
4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 |
# File 'app/models/order.rb', line 4264 def build_early_label_tracking_url(carrier, tracking_number) return nil unless tracking_number.present? case carrier&.downcase when /fedex/ "https://www.fedex.com/fedextrack/?trknbr=#{tracking_number}" when /ups/ "https://www.ups.com/track?tracknum=#{tracking_number}" when /usps/ "https://tools.usps.com/go/TrackConfirmAction?tLabels=#{tracking_number}" when /ontrac/ "https://www.ontrac.com/trackingdetail.asp?tracking=#{tracking_number}" end end |
#buying_group ⇒ BuyingGroup
256 |
# File 'app/models/order.rb', line 256 belongs_to :buying_group, optional: true |
#calculate_actual_shipping_cost ⇒ Object
4512 4513 4514 |
# File 'app/models/order.rb', line 4512 def calculate_actual_shipping_cost deliveries.invoiced.to_a.sum { |d| d.actual_shipping_cost.to_f } end |
#can_auto_reserve_serial_numbers? ⇒ Boolean
1322 1323 1324 |
# File 'app/models/order.rb', line 1322 def can_auto_reserve_serial_numbers? order_type != 'CO' end |
#can_be_cancelled? ⇒ Boolean
2890 2891 2892 2893 2894 2895 |
# File 'app/models/order.rb', line 2890 def can_be_cancelled? return false if editing_locked? return false if is_edi_order? && cancellation_reason.blank? cancelable? end |
#can_be_returned? ⇒ Boolean
1571 1572 1573 |
# File 'app/models/order.rb', line 1571 def can_be_returned? is_regular_order? end |
#can_cr_hold?(current_user = nil) ⇒ Boolean
1713 1714 1715 |
# File 'app/models/order.rb', line 1713 def can_cr_hold?(current_user = nil) CAN_CR_HOLD_STATES.include?(state.to_sym) && all_deliveries_cancelable?(current_user) end |
#can_edit_future_release_date? ⇒ Boolean
1059 1060 1061 |
# File 'app/models/order.rb', line 1059 def can_edit_future_release_date? awaiting_deliveries? && deliveries.for_future_release.any? end |
#cancel_deliveries_pre_pack ⇒ Object
4349 4350 4351 4352 4353 |
# File 'app/models/order.rb', line 4349 def cancel_deliveries_pre_pack deliveries.reload.each do |delivery| delivery.cancel_estimated_packaging if delivery.pre_pack? end end |
#cancel_or_destroy ⇒ Object
2797 2798 2799 |
# File 'app/models/order.rb', line 2797 def cancel_or_destroy cart? ? destroy : cancel end |
#cancelable? ⇒ Boolean
1717 1718 1719 |
# File 'app/models/order.rb', line 1717 def cancelable? CANCELABLE_STATES.include?(state.to_sym) && deliveries.non_quoting.empty? end |
#cannot_delete_reason(account = nil) ⇒ Object
2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 |
# File 'app/models/order.rb', line 2249 def cannot_delete_reason(account = nil) if editing_locked? 'Order cannot be deleted because it is in pending processing or beyond or referenced by authorizations.' elsif payments.any? { |pp| pp.captured? } 'Order cannot be deleted because it is referenced by authorizations.' elsif line_items.any?(&:has_linked_unvoided_rma_item?) 'Order cannot be deleted until all linked RMA items have been voided.' elsif pending? || account&.is_admin? 'Orders that are non-draft can only be deleted by an admin' end end |
#cart_identifier ⇒ Object
2897 2898 2899 |
# File 'app/models/order.rb', line 2897 def cart_identifier reference_number || "SC#{id}" end |
#cart_or_in_shipping_estimate? ⇒ Boolean
2901 2902 2903 |
# File 'app/models/order.rb', line 2901 def cart_or_in_shipping_estimate? cart? || in_shipping_estimate? end |
#catalog ⇒ Object
Alias for Customer#catalog
296 |
# File 'app/models/order.rb', line 296 delegate :catalog, :billing_address, :billing_entity, :company, to: :customer |
#check_payments_status ⇒ Object
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 |
# File 'app/models/order.rb', line 1227 def check_payments_status # Sometimes we manually capture the funds in paypal or stripe and HW doesn't know about it. Or the authorization is expired, etc. # This method checks that and puts the payment in the right status return unless payments.present? return if exclude_from_payment_check # Skip payment verification for orders that only have non-gateway payment types # These payment types don't require reauthorization or gateway status checks: # - PO/VPO: Terms-based, pre-approved credit # - Advance Replacement & RMA Credit: Internal credits from RMA process # - Store Credit: Internal customer credit balance # - CHECK/CASH/WIRE: Manual payments processed offline # - ECHECK: Cannot be captured through gateway, requires manual processing # - BREAD/PLAID: No check methods implemented yet non_gateway_payment_types = [ Payment::PO, Payment::VPO, Payment::ADV_REPL, Payment::RMA_CREDIT, Payment::STORE_CREDIT, Payment::CHECK, Payment::CASH, Payment::WIRE, Payment::ECHECK, Payment::BREAD, Payment::PLAID ] return if payments.all? { |p| p.category.in?(non_gateway_payment_types) } # First credit cards payments.credit_cards.each(&:check_cc_payment_status) # Next Paypal payments payments.paypal_payments.each(&:check_paypal_payment_status) # Next paypal invoice payments.paypal_invoices.each(&:check_paypal_invoice_payment_status) # re-sync the prepayments to deliveries deliveries.each(&:relink_payments) # Reload to ensure we have fresh data after payment status checks # Payment status checks may have changed payment states, and we need # fresh delivery totals and payment associations reload deliveries.reload # after re-authorizing, check there are enough funds to cover the order total if all_funds_available? # nothing to do as all funds are available. This means the total of the order is either authorized or captured already. But not only captured. else if can_pending_payment? && !deliveries.any?(&:pre_pack?) && !deliveries.any?(&:pending_manifest_completion?) InternalMailer.order_with_insuficient_payment(self).deliver_later unless pending_payment? pending_payment! end end end |
#check_sales_rep ⇒ Object (protected)
4591 4592 4593 4594 4595 4596 4597 4598 4599 |
# File 'app/models/order.rb', line 4591 def check_sales_rep # Skip sales rep validation for orders without customers (e.g., Store Transfers use from_store/to_store instead) return if customer.nil? errors.add('Sales rep', 'can only be primary or secondary sales rep for a given customer at a time') if (primary_sales_rep == secondary_sales_rep) && primary_sales_rep return unless secondary_sales_rep && !primary_sales_rep errors.add('Order', 'must first have a primary sales rep to have have a secondary sales rep') end |
#ci_invoice_credit_order? ⇒ Boolean
2379 2380 2381 |
# File 'app/models/order.rb', line 2379 def ci_invoice_credit_order? order_type == CREDIT_ORDER && rma&.original_invoice&.invoice_type == Invoice::CI end |
#clear_shipped_date ⇒ Object
2279 2280 2281 |
# File 'app/models/order.rb', line 2279 def clear_shipped_date update_attribute(:shipped_date, nil) if shipped_date.present? end |
#closed_state? ⇒ Boolean
1709 1710 1711 |
# File 'app/models/order.rb', line 1709 def closed_state? CLOSED_STATES.include?(state.to_sym) end |
#communications ⇒ ActiveRecord::Relation<Communication>
283 |
# File 'app/models/order.rb', line 283 has_many :communications, as: :resource, dependent: :nullify |
#company ⇒ Object
Alias for Customer#company
296 |
# File 'app/models/order.rb', line 296 delegate :catalog, :billing_address, :billing_entity, :company, to: :customer |
#company_review_url ⇒ Object
Reviews.io dynamic link for company review using the order reference as order_id
and the customer CN number as customer_identifier (for CRM context).
1079 1080 1081 |
# File 'app/models/order.rb', line 1079 def company_review_url Api::ReviewsIo::DynamicLinkBuilder.for_order(self) end |
#company_review_url_for_confirmation(email: nil) ⇒ Object
Reviews.io dynamic link for the order confirmation / thank-you page.
Uses the customer email instead of the CN identifier.
1085 1086 1087 |
# File 'app/models/order.rb', line 1085 def company_review_url_for_confirmation(email: nil) Api::ReviewsIo::DynamicLinkBuilder.for_order_confirmation(self, email: email) end |
#complete? ⇒ Boolean
1884 1885 1886 |
# File 'app/models/order.rb', line 1884 def complete? invoiced? end |
#completed_regular_deliveries ⇒ Object
4461 4462 4463 |
# File 'app/models/order.rb', line 4461 def completed_regular_deliveries deliveries.active.select(&:completed_regular_delivery?) end |
#completely_shipped? ⇒ Boolean
4469 4470 4471 |
# File 'app/models/order.rb', line 4469 def completely_shipped? deliveries.active.all?(&:completed_regular_delivery?) end |
#consolidate_revenue ⇒ Object
4497 4498 4499 4500 4501 4502 4503 4504 |
# File 'app/models/order.rb', line 4497 def consolidate_revenue # Method to save the order total that the customer paid at the time of checkout and compare this value to the # value we send as a conversion to google ads. return unless online_order? update_column(:revenue_consolidated_at_time_of_checkout, total) update_column(:shipping_cost_at_time_of_checkout, shipping_cost || 0.0) end |
#contact ⇒ Contact
252 |
# File 'app/models/order.rb', line 252 belongs_to :contact, inverse_of: :orders, optional: true |
#contact_combo ⇒ Object
Used by dynamic contact lookup on order creation allowing interaction with the tom-select input on account_managers.html.erb
Contact can either be existing (single integer value for contact_id) or new contact
1036 1037 1038 1039 1040 |
# File 'app/models/order.rb', line 1036 def contact_combo return unless contact_id "Contact|#{contact_id}" end |
#contact_combo=(val) ⇒ Object
Used by dynamic contact lookup on order creation allowing interaction with the tom-select input on account_managers.html.erb
Contact can either be existing (single integer value for contact_id) or new contact if val is in format Customer|customer_id|full_name
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 |
# File 'app/models/order.rb', line 1044 def contact_combo=(val) if val.blank? self.contact = nil return end contact_name, self.contact_id = val.split('|') return unless contact_id.blank? && name.present? self.contact = Contact.create(name: contact_name.squish.titleize, customer_id:) end |
#contact_combo_for_select ⇒ Object
1055 1056 1057 |
# File 'app/models/order.rb', line 1055 def contact_combo_for_select customer.contacts.where(inactive: false).map { |cnt| [cnt.to_s, "Contact|#{cnt.id}"] } end |
#contact_point ⇒ ContactPoint
249 |
# File 'app/models/order.rb', line 249 belongs_to :contact_point, optional: true |
#copy_customer_reps ⇒ Object
1118 1119 1120 1121 1122 |
# File 'app/models/order.rb', line 1118 def copy_customer_reps self.primary_sales_rep_id = primary_sales_rep.try(:id) self.secondary_sales_rep_id = secondary_sales_rep.try(:id) self.local_sales_rep_id = local_sales_rep.try(:id) end |
#copy_invoice_reps ⇒ Object
1108 1109 1110 1111 1112 1113 1114 1115 1116 |
# File 'app/models/order.rb', line 1108 def copy_invoice_reps return unless invoice = invoices.first update( primary_sales_rep_id: invoice.primary_sales_rep_id, secondary_sales_rep_id: invoice.secondary_sales_rep_id, local_sales_rep_id: invoice.local_sales_rep_id ) end |
#copy_items_from(order) ⇒ Boolean
Copy non-shipping line items from another order (typically a guest cart)
into this one, then destroy the source if it is itself a cart.
Atomicity matters: this is the guest -> account cart transfer path that
runs silently on login (Authenticable#handle_cart_transfer). The previous
implementation called add_multiple_items and then unconditionally
order.destroy if order.cart? — so when add_multiple_items raised
Order::ItemNotFound, returned early on editing_locked? / 100-item guard,
or hit a save failure, the source cart was destroyed while the items
never made it into the target. The customer's items vanished at checkout
with no error to the user and (often) no exception report.
Now: bail out early on no-op conditions, wrap copy + destroy in a single
transaction, and on any failure leave both carts untouched and report the
error so we can see it in AppSignal.
1169 1170 1171 1172 1173 1174 1175 1176 1177 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 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 |
# File 'app/models/order.rb', line 1169 def copy_items_from(order) return false if order.nil? sku_array = order.line_items.non_shipping.parents_only.map { |li| { sku: li.sku, qty: li.quantity } } if sku_array.empty? order.destroy! if order.cart? return true end if editing_locked? ErrorReporting.warning( 'Order#copy_items_from aborted: target cart is editing_locked', source: :web, source_cart_id: order.id, target_cart_id: id, target_state: state ) return false end if line_items.size + sku_array.size > 100 ErrorReporting.warning( 'Order#copy_items_from aborted: would exceed 100 line items', source: :web, source_cart_id: order.id, target_cart_id: id, existing: line_items.size, incoming: sku_array.size ) return false end copied = false ActiveRecord::Base.transaction do copied = add_multiple_items(sku_array).present? unless copied ErrorReporting.warning( 'Order#copy_items_from aborted: add_multiple_items returned no items (race with editing_locked? or 100-item guard)', source: :web, source_cart_id: order.id, target_cart_id: id, target_state: state, target_line_item_count: line_items.size ) raise ActiveRecord::Rollback end order.destroy! if order.cart? end copied rescue Order::ItemNotFound, ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, ActiveRecord::RecordNotDestroyed => e ErrorReporting.error( e, source: :web, source_cart_id: order&.id, target_cart_id: id, skus: sku_array.pluck(:sku) ) false end |
#copy_shipping_reference_number_to_deliveries ⇒ Object
1381 1382 1383 1384 1385 1386 1387 |
# File 'app/models/order.rb', line 1381 def copy_shipping_reference_number_to_deliveries # The shipment reference (or FBA ID) can be used as carrier_bol number when present # Only run when shipment_reference_number actually changed and is present return unless saved_change_to_shipment_reference_number? && shipment_reference_number.present? deliveries.update_all(carrier_bol: shipment_reference_number) end |
#country ⇒ Object
1878 1879 1880 1881 1882 |
# File 'app/models/order.rb', line 1878 def country customer.catalog.store.country rescue StandardError nil end |
#create_credit_memo ⇒ Object
2370 2371 2372 2373 |
# File 'app/models/order.rb', line 2370 def create_credit_memo CreditMemo.new_credit_memo_from_order(self) credit_memo.evaluate_taxjar_submission end |
#create_smartfix_ticket ⇒ Object
2453 2454 2455 |
# File 'app/models/order.rb', line 2453 def create_smartfix_ticket new_ss_ticket('SmartFix', ActivityTypeConstants::SSFIX_SERVICE_CONFIRM) end |
#create_smartguide_ticket ⇒ Object
2457 2458 2459 2460 2461 2462 2463 |
# File 'app/models/order.rb', line 2457 def create_smartguide_ticket if line_items.joins(:item).merge(Item.where(sku: 'SGS_ONSITE_FIXRATE')).any? new_ss_ticket('SmartGuide', ActivityTypeConstants::SGS_ONSITE_PREPLAN_MEET) else new_ss_ticket('SmartGuide', ActivityTypeConstants::SGS_REMOTE_PREPLAN_MEET) end end |
#create_smartinstall_ticket ⇒ Object
2449 2450 2451 |
# File 'app/models/order.rb', line 2449 def create_smartinstall_ticket new_ss_ticket('SmartInstall', ActivityTypeConstants::SSI_PREPLAN_MEET) end |
#credit_memo ⇒ CreditMemo
270 |
# File 'app/models/order.rb', line 270 has_one :credit_memo, foreign_key: :credit_order_id |
#credit_memos ⇒ ActiveRecord::Relation<CreditMemo>
278 |
# File 'app/models/order.rb', line 278 has_many :credit_memos, foreign_key: :original_order_id |
#crm_link ⇒ Object
2229 2230 2231 |
# File 'app/models/order.rb', line 2229 def crm_link UrlHelper.instance.order_path(self) end |
#currency_symbol ⇒ Object
1753 1754 1755 |
# File 'app/models/order.rb', line 1753 def currency_symbol Money::Currency.new(currency).symbol end |
#custom_shipping_labels ⇒ Object
Pulls all custom ship labels from the order, deliveries and shipments
2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 |
# File 'app/models/order.rb', line 2806 def custom_shipping_labels label_array = [] label_array += uploads.custom_ship_labels deliveries.each do |d| label_array += d.uploads.custom_ship_labels d.shipments.each do |shipment| label_array += shipment.uploads.custom_ship_labels end end label_array.compact.uniq end |
#customer ⇒ Customer
247 |
# File 'app/models/order.rb', line 247 belongs_to :customer, inverse_of: :orders, optional: true |
#customer_qualifies_for_free_online_shipping? ⇒ Boolean
1588 1589 1590 |
# File 'app/models/order.rb', line 1588 def customer_qualifies_for_free_online_shipping? discounts.free_online_shipping.present? end |
#deep_dup ⇒ Object
486 487 488 489 490 491 492 493 494 495 496 497 |
# File 'app/models/order.rb', line 486 def deep_dup deep_clone( include: [:discounts, :edi_documents], except: %i[txid reference_number created_at creator_id edi_transaction_id edi_po_number] ) do |original, copy| if copy.is_a?(Order) copy.state = 'pending' copy.do_not_detect_shipping = true copy.parent_id = original.id end end end |
#default_billing_emails ⇒ Object
2152 2153 2154 2155 2156 2157 2158 2159 |
# File 'app/models/order.rb', line 2152 def default_billing_emails emails = [] billing_customer = customer.billing_entity emails += NotificationChannel.joins(:contact_point).merge(ContactPoint.emails).where(customer_id: [customer_id, billing_customer&.id].compact.uniq).pluck(ContactPoint[:detail]) emails << billing_customer.contact_points.transmittable.emails.first&.detail if billing_customer.profile && (billing_customer.profile.homeowner? || billing_customer.profile.direct_pro?) emails.compact.uniq.sort end |
#default_credit_card_vault ⇒ CreditCardVault
260 |
# File 'app/models/order.rb', line 260 belongs_to :default_credit_card_vault, class_name: 'CreditCardVault', optional: true |
#deferred_payments_captured_and_ready_for_shipping? ⇒ Boolean
1560 1561 1562 |
# File 'app/models/order.rb', line 1560 def deferred_payments_captured_and_ready_for_shipping? paypal_invoices_paid? && ready_for_shipping? end |
#deletable? ⇒ Boolean
1749 1750 1751 |
# File 'app/models/order.rb', line 1749 def deletable? %w[pending pending_payment crm_back_order in_cr_hold].include? state end |
#deliveries ⇒ ActiveRecord::Relation<Delivery>
290 |
# File 'app/models/order.rb', line 290 has_many :deliveries, -> { order(:origin_address_id) }, dependent: :destroy, autosave: true |
#delivery_activities ⇒ ActiveRecord::Relation<Activity>
275 |
# File 'app/models/order.rb', line 275 has_many :delivery_activities, class_name: 'Activity', through: :deliveries, source: :activities |
#direct_shipments ⇒ ActiveRecord::Relation<Shipment>
279 |
# File 'app/models/order.rb', line 279 has_many :direct_shipments, class_name: 'Shipment' |
#doesnt_already_has_smartinstall? ⇒ Boolean
2332 2333 2334 |
# File 'app/models/order.rb', line 2332 def doesnt_already_has_smartinstall? line_items.smartinstall_items.empty? end |
#download_and_store_early_label_pdf_atomic(shipper, label_result, is_amazon: false) ⇒ Upload?
SAFEGUARD A: Atomic PDF download and storage
Downloads and stores the label PDF with retries, ensuring it's persisted before returning.
This prevents the race condition where void happens before PDF is saved.
Amazon: label data is returned inline from create_label — store directly.
Walmart: requires a separate download_label API call with retries.
3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 |
# File 'app/models/order.rb', line 3924 def download_and_store_early_label_pdf_atomic(shipper, label_result, is_amazon: false) tracking_number = label_result[:tracking_number] carrier = label_result[:carrier] marketplace = is_amazon ? 'Amazon' : 'Walmart' if Rails.env.development? Rails.logger.info('[EarlyLabel] Development mode - creating mock label PDF') return store_mock_early_label_pdf(tracking_number, carrier) end if is_amazon return store_amazon_label_pdf_atomic(label_result, tracking_number, marketplace) end store_walmart_label_pdf_atomic(shipper, label_result, tracking_number, carrier, marketplace) end |
#drop_ship_purchase_orders ⇒ ActiveRecord::Relation<DropShipPurchaseOrder>
292 |
# File 'app/models/order.rb', line 292 has_many :drop_ship_purchase_orders, -> { order(:id) }, through: :deliveries |
#early_label_failure_notification(error_message) ⇒ Object
Send EDI admin notification when early label purchase fails
This ensures failures are not silent and the team is notified
3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 |
# File 'app/models/order.rb', line 3402 def early_label_failure_notification() marketplace = edi_orchestrator_partner&.start_with?('amazon_seller') ? 'Amazon' : 'Walmart' portal = marketplace == 'Amazon' ? 'Amazon Seller Central' : 'Walmart Seller Portal' subject = "Early Label Purchase FAILED - Order #{reference_number}" = <<~MSG Early label purchase failed for #{marketplace} order #{reference_number}. ORDER DETAILS: - Order: #{reference_number} - PO Number: #{edi_po_number} - EDI Partner: #{edi_orchestrator_partner} - Order Link: https://#{CRM_HOSTNAME}/en-US/orders/#{id} ERROR: #{} ACTION REQUIRED: The early label was NOT purchased automatically. The warehouse will need to: 1. Purchase the label manually at ship-label time, OR 2. Purchase via #{portal} and upload as manual_ship_label This order will proceed through normal warehouse flow without early tracking. MSG InternalMailer.notify_edi_admin_of_warning(subject, ).deliver_later Rails.logger.warn("[EarlyLabel] Sent failure notification for order #{reference_number}") rescue StandardError => e Rails.logger.error("[EarlyLabel] Failed to send failure notification: #{e.}") end |
#early_label_flash_message ⇒ Hash
Generate flash messages based on early label purchase result
Called by controllers after order transitions to awaiting_deliveries
3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 |
# File 'app/models/order.rb', line 3604 def return nil unless early_label_purchase_result.present? result = early_label_purchase_result marketplace = edi_orchestrator_partner&.start_with?('amazon_seller') ? 'Amazon' : 'Walmart' if result[:success] if result[:already_purchased] { type: :info, message: "Early shipping label already purchased - Tracking: #{result[:tracking_number]} (#{result[:carrier]})" } else { type: :success, message: "Early shipping label purchased successfully! Tracking: #{result[:tracking_number]} (#{result[:carrier]}) - Tracking has been sent to #{marketplace}." } end else { type: :error, message: "Failed to purchase early shipping label: #{result[:error]}. The order has been released but no tracking was sent to #{marketplace}." } end end |
#early_label_purchase_enabled_for_partner? ⇒ Boolean
Checks whether the partner's orchestrator has early label purchase enabled.
Walmart uses early_label_purchase_enabled?, Amazon uses buy_shipping_enabled?.
3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 |
# File 'app/models/order.rb', line 3523 def early_label_purchase_enabled_for_partner? if edi_orchestrator_partner&.start_with?('walmart_seller') Edi::Walmart::Orchestrator.new(edi_orchestrator_partner.to_sym).early_label_purchase_enabled? elsif edi_orchestrator_partner&.start_with?('amazon_seller') Edi::Amazon::Orchestrator.new(edi_orchestrator_partner.to_sym).buy_shipping_enabled? else false end rescue StandardError => e Rails.logger.warn("[EarlyLabel] Error checking early label purchase enabled: #{e.}") false end |
#early_label_purchased_recently? ⇒ Boolean
Check if early label was purchased recently (within the rapid void threshold)
Used by SAFEGUARD B to prevent race conditions
3274 3275 3276 3277 3278 3279 |
# File 'app/models/order.rb', line 3274 def early_label_purchased_recently? return false unless early_label_purchased_at.present? minutes_since_purchase = (Time.current - early_label_purchased_at) / 60 minutes_since_purchase < EARLY_LABEL_RAPID_VOID_THRESHOLD_MINUTES end |
#early_label_shipments_match?(delivery) ⇒ Hash
Check if current shipments match the early label shipments
Used to detect if warehouse staff changed the packing
3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 |
# File 'app/models/order.rb', line 3863 def early_label_shipments_match?(delivery) return { match: true } unless has_early_purchased_label? current_shipments = delivery.shipments.non_voided.to_a early_count = early_label_shipments_count || 0 early_data = early_label_shipments_data || [] # Check shipment count if current_shipments.size != early_count return { match: false, reason: "Shipment count changed: early label was for #{early_count} shipment(s), now #{current_shipments.size}" } end # Check dimensions/weights for significant changes current_shipments.each_with_index do |shipment, idx| early_shipment = early_data[idx] next unless early_shipment # Check for significant dimension changes (more than 20% or 2 inches) %i[length width height].each do |dim| early_val = early_shipment[dim.to_s]&.to_f || 0 current_val = shipment.send(dim)&.to_f || 0 diff = (early_val - current_val).abs if diff > 2 && diff > (early_val * 0.2) return { match: false, reason: "Shipment #{idx + 1} #{dim} changed significantly: was #{early_val.round(1)}in, now #{current_val.round(1)}in" } end end # Check for significant weight changes (more than 20% or 1 lb) early_weight = early_shipment['weight']&.to_f || 0 current_weight = shipment.weight&.to_f || 0 weight_diff = (early_weight - current_weight).abs if weight_diff > 1 && weight_diff > (early_weight * 0.2) return { match: false, reason: "Shipment #{idx + 1} weight changed significantly: was #{early_weight.round(1)}lbs, now #{current_weight.round(1)}lbs" } end end { match: true } end |
#early_label_upload ⇒ Upload?
Get the early label upload (PDF) if it exists
3585 3586 3587 |
# File 'app/models/order.rb', line 3585 def early_label_upload uploads.in_category('early_ship_label').first end |
#echecks_requiring_authorization ⇒ Object
1733 1734 1735 |
# File 'app/models/order.rb', line 1733 def payments..where(category: Payment::ECHECK).select { |pp| pp.[:required] == true } end |
#edi_cancellation_reason_description ⇒ Object
2616 2617 2618 2619 2620 2621 2622 2623 2624 |
# File 'app/models/order.rb', line 2616 def edi_cancellation_reason_description if is_canadian_tire? # just return 3 character reason code for Canadian Tire cancellation_reason else # here we only want to return a description for when we use a code, fallback to humanize "#{cancellation_reason}: #{(edi_cancellation_reasons.detect { |arr| arr.last == cancellation_reason }&.first || cancellation_reason).to_s.humanize.downcase}" end end |
#edi_cancellation_reasons ⇒ Object
2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 |
# File 'app/models/order.rb', line 2626 def edi_cancellation_reasons if is_amazon_com? [ ['Shipping 100 percent of ordered product', '00'], ['Canceled due to missing/invalid SKU', '02'], ['Canceled out of stock', '03'], ['Buyer request', '99'], ['Canceled due to duplicate Amazon Ship ID', '04'], ['Canceled due to missing/invalid Bill To Location Code', '05'], ['Canceled due to missing/invalid Ship From Location Code', '06'], ['Canceled due to missing/invalid Customer Ship to Name', '07'], ['Canceled due to missing/invalid Customer Ship to Address Line 1 ', '08'], ['Canceled due to missing/invalid Customer Ship to City', '09'], ['Canceled due to missing/invalid Customer Ship to State', '10'], ['Canceled due to missing/invalid Customer Ship to Postal Code ', '11'], ['Canceled due to missing/invalid Customer Ship to Country Code ', '12'], ['Canceled due to missing/invalid Shipping Carrier/Shipping Method ', '13'], ['Canceled due to missing/invalid Unit Price', '20'], ['Canceled due to missing/invalid Ship to Address Line 2', '21'], ['Canceled due to missing/invalid Ship to Address Line 3', '22'], ['Canceled due to Tax Nexus Issue', '50'], ['Canceled due to Restricted SKU/Qty', '51'], ['Canceled due to USPS >$400', '53'], ['Canceled due to Missing AmazonShipID', '54'], ['Canceled due to Missing AmazonOrderID', '55'], ['Canceled due to Missing LineItemId', '56'], ['Canceled due to discontinued item', '71'] ] elsif is_home_depot_usa? [ ['Bad SKU', 'bad_sku'], ["Cancelled at Merchant's Request", 'merchant_request'], ['Invalid Item Cost', 'invalid_item_cost'], ['Invalid method of shipment', 'invalid_ship_method'], ['Merchant detected fraud', 'merchant_detected_fraud'], ['Order Info Missing', 'info_missing'], ['Out of Stock', 'out_of_stock'], ['Product Has Been Discontinued', 'discontinued'], ['Supplier detected fraud', 'supplier_detected_fraud'] ] elsif is_home_depot_can? [ ['Backorder Cancellation', 'backorder_cancel'], ['Bad Address', 'bad_address'], ['Bad SKU', 'bad_sku'], ["Cancelled at Merchant's Request", 'merchant_request'], ['Cannot fulfill the order in time', 'fulfill_time_expired'], ['Cannot Ship as Ordered', 'cannot_meet_all_reqs'], ['Cannot ship to Country', 'cant_shipto_country'], ['Cannot ship to PO Box', 'cannot_shipto_POBOX'], ['Customer Refused Delivery', 'customer_refused'], ['Duplicate Order', 'duplicate_order'], ['Order Entry Error', 'order_entry_error'], ['Order Info Missing', 'info_missing'], ['Other', 'other'], ['Product Has Been Discontinued', 'discontinued'] ] elsif is_costco_ca? [ ['Bad Address', 'bad_address'], ['Bad SKU', 'bad_sku'], ["Cancelled at Merchant's Request", 'merchant_request'], ['Carrier does not service delivery location', 'carrier_does_not_service_area'], ['Customer Changed Mind', 'customer_request'], ['Duplicate Order', 'duplicate_order'], ['Minimum Order Not Met', 'min_order_not_met'], ['Order Entry Error', 'order_entry_error'], ['Order Info Missing', 'info_missing'], ['Out of Stock', 'out_of_stock'], ['Product Has Been Discontinued', 'discontinued'], ['To close order and allow reissue', 'close_and_reissue'], ['Unable to contact recipient', 'unable_to_contact_recipient'] ] elsif is_part_of_lowes_ca? [ ['Bad Address', 'bad_address'], ['Bad SKU', 'bad_sku'], ["Cancelled at Merchant's Request", 'merchant_request'], ['Cannot fulfill the order in time', 'fulfill_time_expired'], ['Cannot Ship as Ordered', 'cannot_meet_all_reqs'], ['Customer Changed Mind', 'customer_request'], ['Invalid Item Cost', 'invalid_item_cost'], ['Order Info Missing', 'info_missing'], ['Out of Stock', 'out_of_stock'], ['Product Has Been Discontinued', 'discontinued'] ] elsif is_part_of_lowes_com? [ ['Bad SKU', 'bad_sku'], ["Cancelled at Merchant's Request", 'merchant_request'], ['Minimum Order Not Met', 'min_order_not_met'], ['Other', 'other'], ['Invalid Item Cost', 'invalid_item_cost'], ['Out of Stock', 'out_of_stock'], ['Product Has Been Discontinued', 'discontinued'] ] elsif is_walmart_ca? [ ['Backorder Cancellation', 'backorder_cancel'], ['Bad SKU', 'bad_sku'], ["Cancelled at Merchant's Request", 'merchant_request'], ['Customer Changed Mind', 'customer_request'], ['Out of Stock', 'out_of_stock'], ['Product Has Been Discontinued', 'discontinued'] ] elsif edi_orchestrator_partner == 'walmart_seller_us' [ ['Backorder Cancellation', 'backorder_cancel'], ['Bad SKU', 'bad_sku'], ["Cancelled at Merchant's Request", 'merchant_request'], ['Customer Changed Mind', 'customer_request'], ['Out of Stock', 'out_of_stock'], ['Product Has Been Discontinued', 'discontinued'] ] elsif is_canadian_tire? [ ['Out of Stock', 'W01'], ['Not Enough Stock', 'W13'], ['Discontinued Item', 'A83'], ['Incorrect Address', 'A03'], ['Invalid Ship Instructions', '051'], ["Can't Ship on Time", 'D50'], ['Cancelled at Retailer Request', 'ABN'], ['Other', 'A13'], ['Bad Sku', 'A80'], ['Cannot ship to country', 'A05'], ['Cannot ship to PO box', 'A06'], ['Cannot ship USPS', 'A82'], ['Carrier does not service delivery location', 'D01'], ['Duplicate order', 'A07'], ['Invalid UOM', 'SOW'], ['Item Recall', 'IV1'], ['Minimum order not met', 'MIN'], ['Order entry error', 'W05'], ['Order info missing', 'B14'], ['Preorder cancellation', 'POA'], ['Fraud', '030'], ['To close order and allow reissue', 'RUN'], ['Unable to contact recipient', 'A58'] ] else %w[ bad_address bad_sku merchant_request carrier_wont_svc_loc customer_request customer_refused duplicate_order info_missing out_of_stock discontinued close_and_reissue ] end end |
#edi_communication_logs ⇒ ActiveRecord::Relation<EdiCommunicationLog>
289 |
# File 'app/models/order.rb', line 289 has_many :edi_communication_logs, through: :edi_documents |
#edi_documents ⇒ ActiveRecord::Relation<EdiDocument>
288 |
# File 'app/models/order.rb', line 288 has_many :edi_documents, dependent: :destroy |
#edi_force_price_match ⇒ Object
1627 1628 1629 1630 1631 1632 1633 |
# File 'app/models/order.rb', line 1627 def edi_force_price_match errs = [] line_items.parents_only.where.not(edi_unit_cost: nil).each do |li| errs << li.errors. unless li.update(price: li.edi_unit_cost) && li.update(discounted_price: li.edi_unit_cost) end errs end |
#edi_orchestrator ⇒ Object
4564 4565 4566 4567 |
# File 'app/models/order.rb', line 4564 def edi_orchestrator # simple order to orchestrator mapping, nil otherwise Edi::BaseOrchestrator.orchestrator_for_customer_id(customer_id) if edi_transaction_id end |
#edi_price_matcheable? ⇒ Boolean
1617 1618 1619 1620 1621 1622 1623 1624 1625 |
# File 'app/models/order.rb', line 1617 def edi_price_matcheable? return false unless is_edi_order? target_lines = line_items.goods.parents_only return false unless target_lines.present? return false unless target_lines.all?(&:edi_unit_cost) target_lines.any? { |li| li.price != li.edi_unit_cost || li.discounted_price != li.edi_unit_cost } end |
#editing_locked? ⇒ Boolean
1705 1706 1707 |
# File 'app/models/order.rb', line 1705 def editing_locked? (LOCKED_STATES.include?(state.to_sym) || purchase_order&.should_lock_linked_order?).to_b end |
#effective_date_for_coupon ⇒ Object
1296 1297 1298 1299 1300 |
# File 'app/models/order.rb', line 1296 def effective_date_for_coupon return Date.current if cart? override_coupon_date.presence || created_at.try(:to_date) || Date.current end |
#email_for_order_confirmation ⇒ Object
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 |
# File 'app/models/order.rb', line 2571 def email_for_order_confirmation party_for_order_confirmation.email || customer.email || (begin party_for_order_confirmation.account.email rescue StandardError nil end) || (begin customer.account.email rescue StandardError nil end) end |
#email_options_for_tracking_email ⇒ Object
2849 2850 2851 |
# File 'app/models/order.rb', line 2849 def [customer.all_emails, tracking_email].flatten.compact.uniq end |
#empty? ⇒ Boolean
1745 1746 1747 |
# File 'app/models/order.rb', line 1745 def empty? line_items.non_shipping.empty? end |
#encrypted_id ⇒ Object
4434 4435 4436 |
# File 'app/models/order.rb', line 4434 def encrypted_id Encryption.encrypt_string(id.to_s) end |
#errors_with_deliveries_errors ⇒ Object
4520 4521 4522 |
# File 'app/models/order.rb', line 4520 def errors_with_deliveries_errors (errors. + deliveries.map { |d| d.errors. }).flatten end |
#estimate_next_available_date_from_out_of_stock_items ⇒ Object
4383 4384 4385 4386 4387 4388 4389 4390 4391 |
# File 'app/models/order.rb', line 4383 def estimate_next_available_date_from_out_of_stock_items # this returns nil if no items are out of stock, or latest available store item's next available date line_items.goods.select do |li| li.stock_status == :none end.map do |li| # Use depth-limited version to prevent infinite recursion li.catalog_item.store_item.next_available_with_depth_limit(max_depth: 10)&.next_available_date end.compact.max_by { |d| d } end |
#find_gbraid ⇒ Object
4560 4561 4562 |
# File 'app/models/order.rb', line 4560 def find_gbraid visit&.gbraid || quote&.visit&.gbraid || opportunity&.visit&.gbraid || customer.visit&.gbraid || customer.visits.where.not(gbraid: nil).last&.gbraid end |
#find_gclid ⇒ Object
4552 4553 4554 |
# File 'app/models/order.rb', line 4552 def find_gclid visit&.gclid || quote&.visit&.gclid || opportunity&.visit&.gclid || customer.gclid || customer.visit&.gclid || customer.visits.where.not(gclid: nil).last&.gclid end |
#find_wbraid ⇒ Object
4556 4557 4558 |
# File 'app/models/order.rb', line 4556 def find_wbraid visit&.wbraid || quote&.visit&.wbraid || opportunity&.visit&.wbraid || customer.visit&.wbraid || customer.visits.where.not(wbraid: nil).last&.wbraid end |
#fire_early_label_edi_confirm(delivery, label_result) ⇒ Object
Fire EDI ship confirm with early label tracking info.
This sends tracking to the marketplace immediately, without waiting for warehouse processing.
Branches for Amazon (packageDetail format) vs Walmart (orderShipment format).
4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 |
# File 'app/models/order.rb', line 4089 def fire_early_label_edi_confirm(delivery, label_result) return unless is_edi_order? Rails.logger.info("[EarlyLabel] Firing EDI ship confirm for order #{reference_number}") begin if edi_orchestrator_partner&.start_with?('amazon_seller') # Buy Shipping V2 purchaseShipment automatically notifies Amazon about the # shipment — a separate confirmShipment via the Orders API is redundant and # fails with "PackageToUpdateNotFound" because Buy Shipping manages packages # in a different subsystem than the Orders API expects. Rails.logger.info("[EarlyLabel] Skipping EDI ship confirm for Amazon Buy Shipping order #{reference_number} — Buy Shipping V2 handles notification automatically") else fire_early_label_edi_confirm_walmart(delivery, label_result) end rescue StandardError => e Rails.logger.error("[EarlyLabel] Failed to fire EDI ship confirm for order #{reference_number}: #{e.}") Rails.logger.error(e.backtrace.first(5).join("\n")) end end |
#fire_early_label_edi_confirm_amazon(delivery, label_result) ⇒ Object
Amazon early label ship confirm — mirrors Edi::Amazon::ConfirmMessageProcessor#acknowledge_order
but builds the message from order data since no shipment record exists yet.
4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 |
# File 'app/models/order.rb', line 4112 def fire_early_label_edi_confirm_amazon(delivery, label_result) orchestrator = Edi::Amazon::Orchestrator.new(edi_orchestrator_partner.to_sym) label_data = label_result.respond_to?(:to_h) ? label_result.to_h : label_result tracking_number = label_data[:tracking_number] effective_carrier = label_data[:carrier] carrier_code = Edi::Amazon::ConfirmMessageProcessor::CARRIER_TO_CARRIER_CODE_MAP_HASH[effective_carrier.to_sym] || 'Other' = raise 'edi_original_order_message is blank — cannot build ship confirm' if .blank? order_hash = JSON.parse().with_indifferent_access order_items = (order_hash[:OrderItems] || []).map do |item| { orderItemId: item[:OrderItemId], quantity: (item[:QuantityOrdered] || 1).to_i } end = { packageDetail: { packageReferenceId: '1', carrierCode: carrier_code, carrierName: effective_carrier, shippingMethod: delivery.shipping_method_friendly, trackingNumber: tracking_number, shipDate: Time.current.iso8601, orderItems: order_items }, codCollectionMethod: 'DirectPayment', marketplaceId: orchestrator.marketplace } ecl = EdiCommunicationLog.create!( partner: orchestrator.partner, category: 'order_confirm', data: .to_json, data_type: 'json', file_info: { order_id: id, reference_number: reference_number, lines_confirmed: order_items.size, early_label: true }, transaction_id: edi_transaction_id, transmit_datetime: Time.current ) ecl.edi_documents.create!(order: self) Rails.logger.info("[EarlyLabel] Created Amazon EDI ship confirm ECL #{ecl.id} for order #{reference_number}") # Immediately send via ConfirmMessageSender (POSTs to orders/v0/orders/{orderId}/shipmentConfirmation) orchestrator..process(ecl) end |
#fire_early_label_edi_confirm_walmart(delivery, label_result) ⇒ Object
Walmart early label ship confirm — original Walmart-specific flow
4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 |
# File 'app/models/order.rb', line 4166 def fire_early_label_edi_confirm_walmart(delivery, label_result) orchestrator = Edi::Walmart::Orchestrator.new(edi_orchestrator_partner.to_sym) = raise 'edi_original_order_message is blank — cannot build Walmart ship confirm' if .blank? order_hash = JSON.parse().with_indifferent_access order_lines = order_hash.dig(:orderLines, :orderLine) || [] label_data = label_result.respond_to?(:to_h) ? label_result.to_h : label_result carrier = label_data[:carrier] tracking_number = label_data[:tracking_number] ship_datetime = Time.current.iso8601 carrier_info = build_early_label_carrier_info(label_data, delivery) tracking_url = build_early_label_tracking_url(carrier, tracking_number) shipped_order_lines = order_lines.map do |line| line_number = line[:lineNumber] quantity = line.dig(:orderLineQuantity, :amount) || '1' { lineNumber: line_number, orderLineStatuses: { orderLineStatus: [ { status: 'Shipped', statusQuantity: { unitOfMeasurement: 'EACH', amount: quantity.to_s }, trackingInfo: { shipDateTime: ship_datetime, carrierName: carrier_info[:carrierName], methodCode: carrier_info[:methodCode], trackingNumber: tracking_number, trackingURL: tracking_url }.compact } ] } } end = { orderShipment: { orderLines: { orderLine: shipped_order_lines } } } ecl = EdiCommunicationLog.create!( partner: orchestrator.partner, category: 'order_confirm', data: .to_json, data_type: 'json', file_info: { order_id: id, reference_number: reference_number, lines_confirmed: shipped_order_lines.size, early_label: true }, transaction_id: edi_transaction_id, transmit_datetime: Time.current ) ecl.edi_documents.create!(order: self) Rails.logger.info("[EarlyLabel] Created Walmart EDI ship confirm ECL #{ecl.id} for order #{reference_number}") ecl.process end |
#first_po_number ⇒ Object
1951 1952 1953 |
# File 'app/models/order.rb', line 1951 def first_po_number payments.detect(&:po_number)&.po_number end |
#first_tracking_email ⇒ Object
4473 4474 4475 |
# File 'app/models/order.rb', line 4473 def first_tracking_email tracking_email&.first end |
#fix_future_release_date ⇒ Object
4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 |
# File 'app/models/order.rb', line 4524 def fix_future_release_date res = {} if requested_ship_on_or_after && (requested_ship_on_or_after > Date.current) && (future_release_date.nil? || (future_release_date < requested_ship_on_or_after)) # check if we are shipping too soon and don't have a future date set or have one set too soon res[:message] = "Order ship on or after date was set past today (#{requested_ship_on_or_after}) and order future release date is set to #{future_release_date || 'none'}, so order future release date was updated to match." update(future_release_date: requested_ship_on_or_after) end # If future_release_date is after requested_ship_before, the user explicitly set a future hold. # Clear requested_ship_before since it conflicts with the intentional future release. # (Previously this would override the user's future_release_date, causing unexpected immediate release) if requested_ship_before && future_release_date && (future_release_date > requested_ship_before) res[:message] = "Order future release date (#{future_release_date}) is after the ship before date (#{requested_ship_before}). Clearing ship before date to respect the future release hold." update(requested_ship_before: nil) end res end |
#formatted_po_number ⇒ Object
2294 2295 2296 |
# File 'app/models/order.rb', line 2294 def formatted_po_number po_number(include_po_prefix: include_po_prefix?) end |
#from_store ⇒ Store
264 |
# File 'app/models/order.rb', line 264 belongs_to :from_store, class_name: 'Store', optional: true |
#fully_funded_by_advance_replacement? ⇒ Boolean
1966 1967 1968 |
# File 'app/models/order.rb', line 1966 def fully_funded_by_advance_replacement? (deliveries.count > 0) && deliveries.all? { |dq| (dq.payments.count > 0) && dq.payments.all? { |pp| pp.category == Payment::ADV_REPL } } end |
#funded_by_advance_replacement? ⇒ Boolean
1970 1971 1972 |
# File 'app/models/order.rb', line 1970 def funded_by_advance_replacement? deliveries.any? { |_dq| payments.any? { |pp| pp.category == Payment::ADV_REPL } } end |
#funded_by_cod? ⇒ Boolean
1962 1963 1964 |
# File 'app/models/order.rb', line 1962 def funded_by_cod? deliveries.all?(&:funded_by_cod?) && (deliveries.count > 0) end |
#funds_available_and_ready_for_warehouse? ⇒ Boolean (protected)
4605 4606 4607 |
# File 'app/models/order.rb', line 4605 def funds_available_and_ready_for_warehouse? all_funds_available? && ready_for_warehouse? end |
#generate_spiff_training_activity ⇒ Object
1465 1466 1467 |
# File 'app/models/order.rb', line 1465 def generate_spiff_training_activity Activity.create(activity_type_id: ActivityTypeConstants::SPIFFACTTRAIN, target_datetime: 7.days.from_now, assigned_resource: primary_sales_rep, party: customer, resource: self) end |
#get_expected_ship_date_time ⇒ Object
4413 4414 4415 4416 |
# File 'app/models/order.rb', line 4413 def get_expected_ship_date_time days_committment = (deliveries.first&.selected_shipping_cost&.days_commitment || 4.0).ceil # put in some fallback of 4 days days_committment.working.days.since(get_scheduled_ship_date_time) end |
#get_scheduled_ship_date_time ⇒ Object
4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 |
# File 'app/models/order.rb', line 4393 def get_scheduled_ship_date_time order_ship_date_time = future_release_date if future_release_date.present? && (future_release_date > Time.current) if crm_back_order? order_ship_date_time = estimate_next_available_date_from_out_of_stock_items || 10.working.days.since(Time.current) # is 10 days a good fallback? elsif order_ship_date_time.nil? Time.use_zone(store.time_zone_string) do # WY Canada is Eastern Time, Wy US is Central today_cut_off_time = Time.zone.parse('15:55:00') order_ship_date = (future_release_date || today_cut_off_time).strftime('%Y-%m-%d') # use future release date if present or today_cut_off_time order_ship_time = today_cut_off_time.strftime('%R:%S') if Time.current > today_cut_off_time || today_cut_off_time.on_weekend? early_pickup_time = Time.zone.parse('10:00:00') order_ship_date = 1.working.day.since(early_pickup_time).strftime('%Y-%m-%d') order_ship_time = early_pickup_time.strftime('%R:%S') end order_ship_date_time = Time.zone.parse("#{order_ship_date} #{order_ship_time}") end end order_ship_date_time.to_time end |
#has_authorized_payment? ⇒ Boolean
1223 1224 1225 |
# File 'app/models/order.rb', line 1223 def payments..any? end |
#has_committed_serial_number_reservations? ⇒ Boolean
2140 2141 2142 |
# File 'app/models/order.rb', line 2140 def has_committed_serial_number_reservations? line_items.any? { |li| li.require_reservation? && !li.all_reserved_serial_numbers_available? } end |
#has_custom_packing_slip? ⇒ Boolean
1853 1854 1855 |
# File 'app/models/order.rb', line 1853 def has_custom_packing_slip? uploads.in_category('custom_packing_slip_pdf').present? || amzbs_packing_slip_included? end |
#has_early_purchased_label? ⇒ Boolean
Check if order has an active (non-voided) early-purchased label
3436 3437 3438 |
# File 'app/models/order.rb', line 3436 def has_early_purchased_label? early_label_tracking_number.present? && early_label_voided_at.nil? end |
#has_incomplete_reservations? ⇒ Boolean
2132 2133 2134 |
# File 'app/models/order.rb', line 2132 def has_incomplete_reservations? has_unreserved_line_items? || has_committed_serial_number_reservations? end |
#has_selected_heated_items? ⇒ Boolean
2340 2341 2342 |
# File 'app/models/order.rb', line 2340 def has_selected_heated_items? smartinstall_data.present? end |
#has_shipping_method? ⇒ Boolean
1863 1864 1865 |
# File 'app/models/order.rb', line 1863 def has_shipping_method? chosen_shipping_method.present? end |
#has_unreserved_line_items? ⇒ Boolean
2136 2137 2138 |
# File 'app/models/order.rb', line 2136 def has_unreserved_line_items? line_items.any? { |li| li.require_reservation? && !li.fully_reserved? } end |
#has_web_rooms_needing_installation_plans? ⇒ Boolean
2144 2145 2146 |
# File 'app/models/order.rb', line 2144 def has_web_rooms_needing_installation_plans? room_configurations.any? { |rc| rc.web_room? && rc.room_layout_attached? && !rc.complete? && !rc.installation_plans_attached? } end |
#hold_for_early_label_mismatch!(delivery, reason) ⇒ Object
Puts the order on CR hold with a descriptive note when early label
purchase cannot proceed because the selected shipping rate doesn't
match available marketplace rates.
3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 |
# File 'app/models/order.rb', line 3564 def hold_for_early_label_mismatch!(delivery, reason) note_text = <<~NOTE.strip ⚠ Early label purchase BLOCKED: #{reason} Please select a valid marketplace shipping rate (e.g. an AMZBS rate for Amazon orders) on the Shipping tab, then release the order. NOTE if can_cr_hold? cr_hold quick_note(note_text) if respond_to?(:quick_note) else quick_note(note_text) if respond_to?(:quick_note) Rails.logger.warn("[EarlyLabel] Cannot CR-hold order #{reference_number} (state=#{state}) — note added instead") end early_label_failure_notification(reason) end |
#hold_order_reasons ⇒ Object
1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 |
# File 'app/models/order.rb', line 1765 def hold_order_reasons res = [] res << 'Order was manually held and so can only be manually released.' if is_manual_hold? res << 'Customer is in a state requiring orders to be held (lead_qualify, guest, bankrupt or closed)' if customer.lead_qualify? || customer.guest? || customer.bankrupt? || customer.closed? res << 'Logged in customer placing order has qc orders flag set' if is_online? && customer.qc_orders? && !CurrentScope.employee_logged_in? res << 'Customer has no billing address' if billing_address.nil? res << 'Order has no shipping method' unless has_shipping_method? if all_rooms_not_orderable? # we should let empty rooms through as long as other rooms in the order are orderable, case in point a tiny corner/closet room that we could not fit heating elements into res << 'Rooms/Heated Spaces are not orderable, empty line items' end res << 'Order ships via freight but shipping address is not freight ready, please set shipping address freight fields and recalculate shipping' if ships_freight_but_address_not_freight_ready? res << 'Deliveries are awaiting packaging estimates' if deliveries.any?(&:pre_pack?) if is_edi_order? res << "EDI order price match discrepancy, expects line total to be #{price_match}" if price_match.present? && price_match != line_total res << 'EDI order missing line number, all non shipping lines must have an edi line number' if line_items.parents_only.non_shipping.where(edi_line_number: nil).present? # Skip shipping option validation when order is being cancelled - shipping method doesn't matter for cancellations unless cancellation_reason.present? if !edi_original_ship_code.to_s.upcase.include?('UNSP') && edi_shipping_option_name.present? && deliveries.any? { |d| !d.shipping_option_matches?(edi_shipping_option_name) } # unless carrier is unspecified ie UNSP, force match on mapped edi_shipping_option_name res << "EDI order must use original shipping option: #{edi_shipping_option_name}" end # For Walmart SWW orders, allow any WalmartSeller shipping option even if edi_shipping_option_name wasn't set # This handles orders created before the 'sww' shipping option name was added walmart_sww_ok = edi_orchestrator_partner&.start_with?('walmart_seller') && deliveries.all? { |d| d.shipping_option&.carrier == 'WalmartSeller' } # For Amazon Buy Shipping orders, allow any AmazonSeller shipping option amazon_amzbs_ok = edi_orchestrator_partner&.start_with?('amazon_seller') && deliveries.all? { |d| d.shipping_option&.carrier == 'AmazonSeller' } marketplace_ok = walmart_sww_ok || amazon_amzbs_ok res << "EDI order must use original shipping option matched from: #{edi_original_ship_code}" if edi_original_ship_code.present? && !edi_shipping_option_name.present? && !marketplace_ok end if deliveries.any? { |d| (!d.signature_confirmation && signature_confirmation?) || (d.signature_confirmation && !signature_confirmation?) } res << "EDI order deliveries must use EDI order signature_confirmation option: #{signature_confirmation}" end if edi_is_pick_slip_required? && !has_custom_packing_slip? link_snippet = '' if customer&.is_houzz? link_snippet = ", get it manually here: <a href='https://www.houzz.com//printBuyerOrder//orderId=#{edi_po_number}' target=_new><i text='https://www.houzz.com//printBuyerOrder//orderId=#{edi_po_number}' class='fas fa-arrow-up-right-from-square'></i> https://www.houzz.com//printBuyerOrder//orderId=#{edi_po_number}</a>" end if customer&.is_amazon_seller_central? link_snippet = ", get it manually here: <a href='https://sellercentral.amazon.com/orders/packing-slip?orderId=#{edi_po_number}' target=_new><i text='https://sellercentral.amazon.com/orders/packing-slip?orderId=#{edi_po_number}' class='fas fa-arrow-up-right-from-square'></i> https://sellercentral.amazon.com/orders/packing-slip?orderId=#{edi_po_number}</a>" end if customer&.is_amazon_vendor_central? link_snippet = ", get it manually here: <a href='https://vendorcentral.amazon.com/hz/vendor/members/df/orders?id=#{edi_po_number}' target=_new><i text='https://vendorcentral.amazon.com/hz/vendor/members/df/orders?id=#{edi_po_number}' class='fas fa-arrow-up-right-from-square'></i> https://vendorcentral.amazon.com/hz/vendor/members/df/orders?id=#{edi_po_number}</a>" end res << "This EDI order requires a custom packing slip, but it failed to get attached#{link_snippet}" end # Home depot canada rule + item ERT240-1.5x35, remove trap when order has been found. BYPASS in notes for false positive if customer.id == 10_358 && line_items.any? { |li| li.item_id == 1910 } && !activities.notes_only.where("notes ILIKE '%BYPASS%'").exists? res << 'EDI order for HDC contains item: ERT240-1.5x35, contact india to ensure not a duplicate of PO 0088974632' end if .present? && cancellation_reason.present? res << "EDI order is pending cancellation via EDI for reason: #{cancellation_reason}, if it is canceled, Heatwave will send a rejection acknowledgement message due to #{cancellation_reason}." bad_vendor_skus_msg = nil bad_merchant_skus_msg = nil bad_vendor_skus_msg = "bad vendor SKUs: #{[:bad_vendor_skus].join(', ')}" if [:bad_vendor_skus]&.any? bad_merchant_skus_msg = "bad merchant SKUs: #{[:bad_merchant_skus].join(', ')}" if [:bad_merchant_skus]&.any? res << "The following bad SKUs need to be addressed because order line items could not be created for them: #{[bad_vendor_skus_msg, bad_merchant_skus_msg].compact.join(', ')}" if bad_vendor_skus_msg || bad_merchant_skus_msg end if shipping_cost.to_f > 0.0 && customer&.bill_shipping_to_customer? res << "EDI order should have shipping cost of $0.00, since EDI customer is set to bill shipping to customer, but order shipping cost is: $#{format('%.2f', shipping_cost.to_f)}. Please ensure to use the EDI customer shipping account or, worst case, have an admin or manager add either a EDI_SHIPPING_ADJ or FS-A coupon." end end # NOTE: "Shipping too late" is now a warning, not a blocking hold reason. # It's displayed via shipping_date_warnings but doesn't prevent order release. # Users can still ship late orders after acknowledging the warning. res << 'Cannot combine service items with goods in the same order. Please split this order.' if line_items.services.with_positive_qty.any? && line_items.goods.with_positive_qty.any? if (moqv = minimum_order_quantity_violations).present? res += moqv.map(&:name) end res += errors. unless ready_for_shipping? res.uniq end |
#hold_orders? ⇒ Boolean
1757 1758 1759 |
# File 'app/models/order.rb', line 1757 def hold_orders? hold_order_reasons.present? end |
#human_state_name ⇒ Object
1898 1899 1900 1901 1902 1903 1904 |
# File 'app/models/order.rb', line 1898 def human_state_name if deliveries.any?(&:service_ready_to_fulfill?) and awaiting_deliveries? 'awaiting service delivery' else super end end |
#include_po_prefix? ⇒ Object
Alias for Customer#include_po_prefix?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#includes_schedulable_service? ⇒ Boolean
2029 2030 2031 |
# File 'app/models/order.rb', line 2029 def includes_schedulable_service? belongs_to_smartservice_group? end |
#inherited_attention_name ⇒ Object
2593 2594 2595 2596 2597 |
# File 'app/models/order.rb', line 2593 def inherited_attention_name n = nil n = shipping_address.person_name if shipping_address && customer&.is_person? n end |
#installation_country_iso ⇒ Object
2845 2846 2847 |
# File 'app/models/order.rb', line 2845 def installation_country_iso opportunity.presence&.installation_country_iso end |
#installation_country_iso3 ⇒ Object
2841 2842 2843 |
# File 'app/models/order.rb', line 2841 def installation_country_iso3 opportunity.presence&.installation_country_iso3 end |
#installation_is_within_range? ⇒ Boolean
2344 2345 2346 2347 2348 2349 2350 |
# File 'app/models/order.rb', line 2344 def installation_is_within_range? zip_code = shipping_address&.zip distance = SmartServicesController.helpers.calculate_distance_from_lz(zip_code) return true if distance.present? and distance <= 100 # within 100 miles from office false end |
#installation_postal_code ⇒ Object
2833 2834 2835 |
# File 'app/models/order.rb', line 2833 def installation_postal_code opportunity.presence&.installation_postal_code end |
#installation_state_code ⇒ Object
2837 2838 2839 |
# File 'app/models/order.rb', line 2837 def installation_state_code opportunity.presence&.installation_state_code end |
#invoice_balance ⇒ Object
1943 1944 1945 1946 1947 1948 1949 |
# File 'app/models/order.rb', line 1943 def invoice_balance bal = BigDecimal('0.0') invoices.to_a.each do |i| bal += i.balance end bal end |
#invoiced_local_sales_rep ⇒ Party
263 |
# File 'app/models/order.rb', line 263 belongs_to :invoiced_local_sales_rep, class_name: 'Party', foreign_key: :local_sales_rep_id, optional: true |
#invoiced_primary_sales_rep ⇒ Party
261 |
# File 'app/models/order.rb', line 261 belongs_to :invoiced_primary_sales_rep, class_name: 'Party', foreign_key: :primary_sales_rep_id, optional: true |
#invoiced_secondary_sales_rep ⇒ Party
262 |
# File 'app/models/order.rb', line 262 belongs_to :invoiced_secondary_sales_rep, class_name: 'Party', foreign_key: :secondary_sales_rep_id, optional: true |
#invoices ⇒ ActiveRecord::Relation<Invoice>
280 |
# File 'app/models/order.rb', line 280 has_many :invoices |
#is_amazon_com? ⇒ Object
Alias for Customer#is_amazon_com?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_amazon_seller_central? ⇒ Object
Alias for Customer#is_amazon_seller_central?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_canadian_tire? ⇒ Object
Alias for Customer#is_canadian_tire?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_costco_ca? ⇒ Object
Alias for Customer#is_costco_ca?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_crm? ⇒ Boolean
2599 2600 2601 |
# File 'app/models/order.rb', line 2599 def is_crm? order_reception_type == 'CRM' end |
#is_edi_order? ⇒ Boolean
1604 1605 1606 |
# File 'app/models/order.rb', line 1604 def is_edi_order? edi_transaction_id.present? end |
#is_fba? ⇒ Boolean
1639 1640 1641 |
# File 'app/models/order.rb', line 1639 def is_fba? is_store_transfer? && customer&.is_amazon_com? end |
#is_from_myprojects? ⇒ Boolean
1874 1875 1876 |
# File 'app/models/order.rb', line 1874 def is_from_myprojects? !customer_reference.to_s.strip.empty? end |
#is_home_depot_can? ⇒ Object
Alias for Customer#is_home_depot_can?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_home_depot_usa? ⇒ Object
Alias for Customer#is_home_depot_usa?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_houzz? ⇒ Object
Alias for Customer#is_houzz?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_marketing_order? ⇒ Boolean
2399 2400 2401 |
# File 'app/models/order.rb', line 2399 def is_marketing_order? order_type == MARKETING_ORDER end |
#is_online? ⇒ Boolean
2603 2604 2605 |
# File 'app/models/order.rb', line 2603 def is_online? order_reception_type == 'Online' end |
#is_part_of_lowes_ca? ⇒ Object
Alias for Customer#is_part_of_lowes_ca?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_part_of_lowes_com? ⇒ Object
Alias for Customer#is_part_of_lowes_com?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_price_editable? ⇒ Boolean
Tells whether or not the order can have its line item discounted and msrp price editable by the
price editable concerns, this method is called by the ability check first.
1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 |
# File 'app/models/order.rb', line 1577 def is_price_editable? case order_type when Order::STORE_TRANSFER !editing_locked? when Order::CREDIT_ORDER !(ready_for_printing? || printed?) else false end end |
#is_regular_order? ⇒ Boolean
2403 2404 2405 |
# File 'app/models/order.rb', line 2403 def is_regular_order? [SALES_ORDER, MARKETING_ORDER, TECH_ORDER].include?(order_type) end |
#is_remote_service? ⇒ Boolean
2053 2054 2055 |
# File 'app/models/order.rb', line 2053 def is_remote_service? belongs_to_smartservice_group? && line_items.any? { |a| a.sku.include?('REMOTE') } end |
#is_rma_replacement? ⇒ Boolean
2387 2388 2389 |
# File 'app/models/order.rb', line 2387 def is_rma_replacement? rma && [SALES_ORDER, MARKETING_ORDER, TECH_ORDER].include?(order_type) end |
#is_rma_return? ⇒ Boolean
2375 2376 2377 |
# File 'app/models/order.rb', line 2375 def is_rma_return? order_type == CREDIT_ORDER end |
#is_sales_order? ⇒ Boolean
2391 2392 2393 |
# File 'app/models/order.rb', line 2391 def is_sales_order? order_type == SALES_ORDER end |
#is_smartfit_service? ⇒ Boolean
2033 2034 2035 |
# File 'app/models/order.rb', line 2033 def is_smartfit_service? line_items.smartfit_items.any? end |
#is_smartfix_service? ⇒ Boolean
2041 2042 2043 |
# File 'app/models/order.rb', line 2041 def is_smartfix_service? line_items.smartfix_items.any? end |
#is_smartguide_service? ⇒ Boolean
2045 2046 2047 |
# File 'app/models/order.rb', line 2045 def is_smartguide_service? line_items.smartguide_items.any? end |
#is_smartinstall_service? ⇒ Boolean
2037 2038 2039 |
# File 'app/models/order.rb', line 2037 def is_smartinstall_service? line_items.smartinstall_items.any? end |
#is_store_transfer? ⇒ Boolean
2383 2384 2385 |
# File 'app/models/order.rb', line 2383 def is_store_transfer? order_type == STORE_TRANSFER end |
#is_subject_to_minimum_qty_rules? ⇒ Boolean
2407 2408 2409 |
# File 'app/models/order.rb', line 2407 def is_subject_to_minimum_qty_rules? [SALES_ORDER].include?(order_type) end |
#is_tech_order? ⇒ Boolean
2395 2396 2397 |
# File 'app/models/order.rb', line 2395 def is_tech_order? order_type == TECH_ORDER end |
#is_walmart_ca? ⇒ Object
Alias for Customer#is_walmart_ca?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#is_warehouse_pickup? ⇒ Boolean
2128 2129 2130 |
# File 'app/models/order.rb', line 2128 def is_warehouse_pickup? shipping_address&.is_warehouse end |
#is_wasn4_or_wat0f? ⇒ Object
Alias for Customer#is_wasn4_or_wat0f?
478 479 |
# File 'app/models/order.rb', line 478 delegate :include_po_prefix?, :is_home_depot_usa?, :is_home_depot_can?, :is_walmart_ca?, :is_amazon_com?, :is_costco_ca?, :is_part_of_lowes_ca?, :is_part_of_lowes_com?, :is_houzz?, :is_wasn4_or_wat0f?, :is_canadian_tire?, :is_amazon_seller_central?, to: :customer |
#job_name ⇒ Object
1552 1553 1554 1555 1556 1557 1558 |
# File 'app/models/order.rb', line 1552 def job_name if quote quote.opportunity.name elsif opportunity opportunity.name end end |
#linked_support_cases ⇒ ActiveRecord::Relation<LinkedSupportCase>
284 |
# File 'app/models/order.rb', line 284 has_many :linked_support_cases, through: :room_configurations, source: :support_cases |
#local_sales_rep ⇒ Object
1406 1407 1408 1409 1410 |
# File 'app/models/order.rb', line 1406 def local_sales_rep return invoiced_local_sales_rep if order_closed? invoiced_local_sales_rep || customer&.local_sales_rep end |
#log_early_label_event(event_type, message, details = {}) ⇒ Object
Log an event to the early label API log
Used for debugging and visibility into what happened during early label purchase
3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 |
# File 'app/models/order.rb', line 3331 def log_early_label_event(event_type, , details = {}) @early_label_api_log ||= [] @early_label_api_log << { timestamp: Time.current.iso8601, event: event_type, message: , details: details } Rails.logger.info("[EarlyLabel] #{event_type}: #{} #{details.present? ? details.to_json : ''}") end |
#mark_serial_coupons_as_used ⇒ Object
4426 4427 4428 |
# File 'app/models/order.rb', line 4426 def mark_serial_coupons_as_used discounts.includes(:coupon_serial_number).joins(:coupon_serial_number).each { |d| d.coupon_serial_number.update_attribute(:used, true) } end |
#marketplace_early_label_eligible? ⇒ Boolean
Check if order is eligible for marketplace early label purchase (Walmart SWW or Amazon Buy Shipping)
3485 3486 3487 |
# File 'app/models/order.rb', line 3485 def marketplace_early_label_eligible? walmart_sww_eligible? || amazon_buy_shipping_eligible? end |
#material_alerts ⇒ ActiveRecord::Relation<MaterialAlert>
287 |
# File 'app/models/order.rb', line 287 has_many :material_alerts, dependent: :destroy |
#merge_shipper_api_log(shipper) ⇒ Object
Merge raw HTTP API call logs from the shipper into the order's API log.
Works for both Walmart SWW and Amazon Buy Shipping carriers.
3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 |
# File 'app/models/order.rb', line 3344 def merge_shipper_api_log(shipper) return unless shipper.respond_to?(:api_log) && shipper.api_log.present? prefix = case shipper when Shipping::AmazonSeller then 'amzbs_http' when Shipping::WalmartSeller then 'sww_http' else 'marketplace_http' end @early_label_api_log ||= [] @early_label_api_log_offset ||= 0 new_entries = shipper.api_log[@early_label_api_log_offset..] @early_label_api_log_offset = shipper.api_log.length (new_entries || []).each do |entry| @early_label_api_log << { timestamp: entry[:timestamp], event: "#{prefix}_#{entry[:operation]}", message: "HTTP #{entry[:method]} #{entry[:url]}", details: { operation: entry[:operation], method: entry[:method], url: entry[:url], request_payload: entry[:request_payload], response_status: entry[:response_status], response_body: entry[:response_body], success: entry[:success], error: entry[:error] } } end end |
#messaging_logs ⇒ ActiveRecord::Relation<MessagingLog>
286 |
# File 'app/models/order.rb', line 286 has_many :messaging_logs, dependent: :destroy, as: :resource |
#minimum_order_quantity_violations ⇒ Object
1849 1850 1851 |
# File 'app/models/order.rb', line 1849 def minimum_order_quantity_violations Item::Materials::Checks::Moq.new.process(container: self).alerts end |
#move_deliveries_from_quoting! ⇒ Object
4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 |
# File 'app/models/order.rb', line 4285 def move_deliveries_from_quoting! deliveries.quoting.each do |delivery| next if delivery.pre_pack? delivery.shipment_instructions = shipment_instructions delivery.label_instructions = label_instructions delivery.future_release_date = future_release_date delivery.manual_release_only = manual_release_only delivery.do_not_reserve_stock = do_not_reserve_stock # Save before ready_to_ship! because ready_to_ship! calls reload inside an advisory lock, # which would discard unsaved changes (including future_release_date). # Without this save, orders with future release dates incorrectly go to at_warehouse # instead of future_release state. delivery.save! delivery.ready_to_ship! end end |
#move_deliveries_to_quoting! ⇒ Object
4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 |
# File 'app/models/order.rb', line 4325 def move_deliveries_to_quoting! deliveries.reload.each do |delivery| Rails.logger.info("[Order] Moving delivery #{delivery.id} from #{delivery.state} to quoting") begin if delivery.can_back_to_quoting? delivery.back_to_quoting! Rails.logger.info("[Order] Successfully moved delivery #{delivery.id} to quoting") else Rails.logger.warn("[Order] Cannot move delivery #{delivery.id} to quoting - transition not allowed from state: #{delivery.state}") # Try to force it by calling cancel first if delivery.cancelable? delivery.cancel delivery.back_to_quoting! if delivery.can_back_to_quoting? Rails.logger.info("[Order] Successfully moved delivery #{delivery.id} to quoting after cancel") end end rescue StandardError => e Rails.logger.error("[Order] Error moving delivery #{delivery.id} to quoting: #{e.}") Rails.logger.error(e.backtrace.first(5).join("\n")) raise # Re-raise to rollback the transaction end end end |
#move_service_case_from_pending_service_payment ⇒ Object
4303 4304 4305 4306 |
# File 'app/models/order.rb', line 4303 def move_service_case_from_pending_service_payment service_case = support_cases&.services&.pending_service_payment&.first service_case.presence&.case_open end |
#name ⇒ Object
2213 2214 2215 2216 2217 2218 2219 2220 2221 |
# File 'app/models/order.rb', line 2213 def name po_numbers_text = '' po_numbers = po_number if po_numbers.present? po_numbers_text = " (PO# #{po_numbers})" unless po_numbers.index(',') po_numbers_text = " (PO#s #{po_numbers})" if po_numbers.index(',') end "Order ##{reference_number}#{po_numbers_text}" end |
#need_to_recalculate_shipping ⇒ Object
4506 4507 4508 4509 4510 |
# File 'app/models/order.rb', line 4506 def need_to_recalculate_shipping self.recalculate_shipping = true self.do_not_detect_shipping = false logger.debug "Deliverable, need_to_recalculate_shipping ID: #{id}, self.recalculate_shipping: #{recalculate_shipping}, self.do_not_detect_shipping: #{do_not_detect_shipping}" end |
#needs_attention_issues ⇒ Object
4418 4419 4420 4421 4422 4423 4424 |
# File 'app/models/order.rb', line 4418 def needs_attention_issues issues = [] issues << "order is #{state.humanize.downcase} but deliveries are still in quoting, need to hold the order and recalculate shipping" if SHIPPING_STATES.include?(state.to_sym) && deliveries.any?(&:quoting?) issues << "order is #{state.humanize.downcase} and deliveries in state Awaiting PO fulfillment, need to ensure PO fulfillment process proceeds" if deliveries.any?(&:awaiting_po_fulfillment?) issues << "order is #{state.humanize.downcase} and deliveries in state Processing PO fulfillment, need to ensure PO fulfillment process completed" if deliveries.any?(&:processing_po_fulfillment?) issues end |
#needs_spiff_training? ⇒ Boolean
1461 1462 1463 |
# File 'app/models/order.rb', line 1461 def needs_spiff_training? spiff_enrollment.present? && spiff_enrollment.spiff.is_active? && (customer.orders.count == 1) && customer.activities.where(activity_type_id: ActivityTypeConstants::SPIFFACTTRAIN).empty? end |
#new_customer_online_order ⇒ Object
1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 |
# File 'app/models/order.rb', line 1643 def new_customer_online_order # puts "!!!new_customer_online_order, new_customer_order: #{new_customer_order}, self.customer.reception_type.downcase: #{self.customer.reception_type.downcase}, self.customer.account.present?: #{self.customer.account.present?}" # Check that this is a new customer/order and an online order Rails.logger.info "new_customer_online_order: id: #{id}, reference_number: #{reference_number}" if new_customer_order && online_order? Rails.logger.info "new_customer_online_order: TRUE id: #{id}, reference_number: #{reference_number}" true else Rails.logger.info "new_customer_online_order: FALSE id: #{id}, reference_number: #{reference_number}" false end end |
#new_customer_order ⇒ Object
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 |
# File 'app/models/order.rb', line 1656 def new_customer_order # puts "!!!new_customer_order, self.customer.orders.length: #{self.customer.orders.length}, !in_management_hold?: #{!in_management_hold?}" Rails.logger.info "new_customer_order: id: #{id}, reference_number: #{reference_number}" # Check that this is a new customer/order and not in manager hold (in the case of Canada situation above) if (customer.orders.length == 1) && !in_management_hold? Rails.logger.info "new_customer_order: TRUE id: #{id}, reference_number: #{reference_number}" true else Rails.logger.info "new_customer_order: FALSE id: #{id}, reference_number: #{reference_number}" false end end |
#new_ss_ticket(service, activity_type) ⇒ Object
2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 |
# File 'app/models/order.rb', line 2465 def new_ss_ticket(service, activity_type) return if support_cases.services.any? ticket = support_cases.new( case_type: 'SmartService', assigned_to_id: Employee::SCOTT, state: 'new', service_address_id: shipping_address.id, description: "#{service}: Order id #{id}. #{service} request for a project.", priority: 'High', service: ) ticket.build_participants_and_rooms(order_id: id) return unless ticket.save! # ticket.order_ids = [self.id] this is already being done in the build_participants_and_rooms method Activity.create(lock_target_datetime: false, activity_type_id: activity_type, target_datetime: 1.working.days.from_now, assigned_resource: Employee.find(Employee::SCOTT), # Assigned by default to JL party: customer, resource: ticket, notes: "Call Customer to confirm date and time of the #{service} service.") end |
#next_six_months_with_end_dates ⇒ Object
1888 1889 1890 1891 1892 1893 1894 1895 1896 |
# File 'app/models/order.rb', line 1888 def next_six_months_with_end_dates today = Date.today (0..5).map do |i| date = today >> i month_name = date.strftime('%B %Y') end_of_month = date.end_of_month.strftime('%Y-%m-%d') [month_name, end_of_month] end end |
#notify_pre_pack_cancellation ⇒ Object
4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 |
# File 'app/models/order.rb', line 4312 def notify_pre_pack_cancellation pre_pack_delivery_ids = deliveries.reload.where(state: "pre_pack").ids return unless pre_pack_delivery_ids.any? cancelled_by_id = PaperTrail.request.whodunnit AfterCommitEverywhere.after_commit do cancelled_by = Party.find_by(id: cancelled_by_id) Delivery.where(id: pre_pack_delivery_ids).find_each do |delivery| delivery.send_delivery_pre_pack_cancelled_notification(cancelled_by: cancelled_by) end end end |
#ok_to_delete?(account = nil) ⇒ Boolean
2244 2245 2246 2247 |
# File 'app/models/order.rb', line 2244 def ok_to_delete?(account = nil) account&.is_admin? || (cancelled? && (line_items.empty? || reference_number.blank?)) end |
#online_order? ⇒ Boolean
1682 1683 1684 1685 |
# File 'app/models/order.rb', line 1682 def online_order? (res = creator.blank? || !creator.is_employee?) && customer.account res end |
#open_activities_counter ⇒ Object
4449 4450 4451 |
# File 'app/models/order.rb', line 4449 def open_activities_counter activities.open_activities.visible_by_default.size end |
#opportunities ⇒ Object
1498 1499 1500 1501 1502 1503 |
# File 'app/models/order.rb', line 1498 def opportunities opps = [] opps << opportunity opps += room_configurations.map(&:opportunity) opps.uniq.compact end |
#opportunity ⇒ Opportunity
251 |
# File 'app/models/order.rb', line 251 belongs_to :opportunity, inverse_of: :orders, optional: true |
#opportunity_name ⇒ Object
1669 1670 1671 |
# File 'app/models/order.rb', line 1669 def opportunity_name opportunity.try(:name) end |
#opportunity_name=(val) ⇒ Object
1673 1674 1675 1676 1677 1678 1679 1680 |
# File 'app/models/order.rb', line 1673 def opportunity_name=(val) if val.present? opp = customer.opportunities.find_or_create_by(name: val.strip) self.opportunity = opp else self.opportunity = nil end end |
#order_closed? ⇒ Boolean
1389 1390 1391 |
# File 'app/models/order.rb', line 1389 def order_closed? invoiced? || cancelled? || fraudulent? || printed? end |
#order_emails ⇒ Object
2161 2162 2163 2164 2165 2166 2167 2168 2169 |
# File 'app/models/order.rb', line 2161 def order_emails emails = [] emails += customer.contacts_and_self_contact_points_by_category('email').pluck(:detail) emails += transmission_email emails += tracking_email emails += contact.contact_points.emails.pluck(:detail) if contact emails += billing_emails || [] emails.compact.uniq.sort end |
#order_type_title ⇒ Object
1356 1357 1358 |
# File 'app/models/order.rb', line 1356 def order_type_title ALL_ORDER_TYPES[order_type] || 'Unknown' end |
#partially_shipped? ⇒ Boolean
4465 4466 4467 |
# File 'app/models/order.rb', line 4465 def partially_shipped? deliveries.active.any?(&:completed_regular_delivery?) end |
#participants_options_for_select ⇒ Object
2882 2883 2884 |
# File 'app/models/order.rb', line 2882 def opportunity&.opportunity_participants&.map { |scp| [scp.party.full_name, scp.party_id] } end |
#party_for_order_confirmation ⇒ Object
2567 2568 2569 |
# File 'app/models/order.rb', line 2567 def party_for_order_confirmation contact || customer end |
#pay_on_spiff? ⇒ Boolean
1318 1319 1320 |
# File 'app/models/order.rb', line 1318 def pay_on_spiff? spiff_enrollment.present? && (spiff_enrollment.spiff.eligible_reward(self) > 0) end |
#payment_method ⇒ Object
1906 1907 1908 1909 1910 |
# File 'app/models/order.rb', line 1906 def payment_method payments..first.category rescue StandardError nil end |
#payment_method_requires_authorization? ⇒ Boolean
1729 1730 1731 |
# File 'app/models/order.rb', line 1729 def .any? end |
#payment_options(source) ⇒ Object
1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 |
# File 'app/models/order.rb', line 1477 def (source) = Payment.(billing_entity, self) .uniq! = [] if %w[cart online_order_payment].include?(source) if includes_schedulable_service? = [Payment::CREDIT_CARD] else = [Payment::CREDIT_CARD, Payment::PAYPAL, Payment::PO, Payment::VPO, Payment::CHECK, Payment::WIRE] end elsif source == 'online_order_invoices' = [Payment::CREDIT_CARD] elsif source == 'crm' = [Payment::CREDIT_CARD, Payment::PO, Payment::VPO, Payment::RMA_CREDIT, Payment::STORE_CREDIT, Payment::PAYPAL_INVOICE, Payment::ECHECK, Payment::ADV_REPL, Payment::CHECK, Payment::WIRE, Payment::CASH] end .each do |po| << po if .include?(po) end end |
#payments ⇒ ActiveRecord::Relation<Payment>
273 |
# File 'app/models/order.rb', line 273 has_many :payments, dependent: :nullify, inverse_of: :order |
#payments_requiring_authorization ⇒ Object
1737 1738 1739 |
# File 'app/models/order.rb', line 1737 def payments..select { |pp| !pp.payment_approved? && (pp.[:required] == true) } end |
#payments_with_fraud_report ⇒ Object
1314 1315 1316 |
# File 'app/models/order.rb', line 1314 def payments_with_fraud_report payments.non_voided.select { |a| a.fraud_report.present? && !a.payment_approved? } end |
#paypal_invoices_paid? ⇒ Boolean
1564 1565 1566 1567 1568 1569 |
# File 'app/models/order.rb', line 1564 def paypal_invoices_paid? active_paypal_invoices = payments.paypal_invoices.where.not(state: %w[declined voided expired cancelled]) return true if active_paypal_invoices.empty? active_paypal_invoices.all?(&:captured?) end |
#po_number(include_po_prefix: false, limit: nil) ⇒ Object
2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 |
# File 'app/models/order.rb', line 2283 def po_number(include_po_prefix: false, limit: nil) query = payments.where(state: %w[authorized captured]).where.not(po_number: [nil, '']) query = query.limit(limit) if limit po_numbers = query.distinct.pluck(:po_number) po_numbers = po_numbers.map(&:strip).uniq res = po_numbers.join(', ') res.insert(0, 'PO# ') if res.present? && include_po_prefix res = purchase_order.reference_number if is_store_transfer? res end |
#po_number_barcode(file_path: nil) ⇒ Object
2321 2322 2323 |
# File 'app/models/order.rb', line 2321 def (file_path: nil) self.class.(po_number:, file_path:) end |
#po_numbers ⇒ Object
2223 2224 2225 |
# File 'app/models/order.rb', line 2223 def po_numbers payments.where.not(po_number: nil).distinct.pluck(:po_number) end |
#potential_fraud? ⇒ Boolean
1302 1303 1304 |
# File 'app/models/order.rb', line 1302 def potential_fraud? payments..any? { |pp| pp.try(:fraud_report).try(:potential_fraud?) && !pp.payment_approved? } end |
#potential_fraud_reasons ⇒ Object
1306 1307 1308 1309 1310 1311 1312 |
# File 'app/models/order.rb', line 1306 def potential_fraud_reasons reasons = [] payments_with_fraud_report.each do |payment| reasons += payment.fraud_report.potential_fraud_reasons end reasons.uniq end |
#precreated_rma_credit_available ⇒ Object
1955 1956 1957 1958 1959 1960 |
# File 'app/models/order.rb', line 1955 def precreated_rma_credit_available total_available = line_total_plus_tax spent = payments..where(category: Payment::RMA_CREDIT).sum(:amount) available = total_available - spent available < 0 ? BigDecimal('0.0') : available end |
#preset_jobs ⇒ ActiveRecord::Relation<PresetJob>
285 |
# File 'app/models/order.rb', line 285 has_many :preset_jobs, inverse_of: :order |
#prevent_recalculate_shipping? ⇒ Boolean
2853 2854 2855 |
# File 'app/models/order.rb', line 2853 def prevent_recalculate_shipping? is_credit_order? || SOLD_STATES.include?(state.to_sym) || retrieving_shipping_costs? end |
#primary_party ⇒ Object
1635 1636 1637 |
# File 'app/models/order.rb', line 1635 def primary_party contact || customer end |
#primary_rep_name ⇒ Object
2148 2149 2150 |
# File 'app/models/order.rb', line 2148 def primary_rep_name primary_sales_rep.try(:full_name) || 'Unassigned' end |
#primary_sales_rep ⇒ Object
1393 1394 1395 1396 1397 1398 |
# File 'app/models/order.rb', line 1393 def primary_sales_rep # if it's closed always return what's stored (even if blank) return invoiced_primary_sales_rep if order_closed? invoiced_primary_sales_rep || customer&.primary_sales_rep end |
#product_review_url ⇒ Object
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 |
# File 'app/models/order.rb', line 1089 def product_review_url return nil unless customer&.email.present? product_skus = line_items.non_shipping.joins(:item).pluck('items.sku').compact.uniq return nil unless product_skus.any? store_key = 'warmlyyours-com' params = { store: store_key, user: ERB::Util.url_encode(customer.full_name.presence || 'Customer'), order_id: ERB::Util.url_encode(reference_number), email: ERB::Util.url_encode(customer.email), products: ERB::Util.url_encode(product_skus.join(';')), type: 'product_review', rating: 5 } "https://www.reviews.io/store/landing_new_review?#{params.map { |k, v| "#{k}=#{v}" }.join('&')}" end |
#prune_cart_rooms ⇒ Object
2361 2362 2363 2364 2365 2366 2367 2368 |
# File 'app/models/order.rb', line 2361 def prune_cart_rooms # this method clears out associated rooms from the cart if none of the room line items remain # this is only for carts since we want to keep room association when linking rooms to quotes and orders in the crm before the rooms have been designed rcs = room_configurations rcs.each do |rc| remove_room_configuration(rc) if line_items.where(room_configuration_id: rc.id).empty? end end |
#public_path ⇒ Object
2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 |
# File 'app/models/order.rb', line 2233 def public_path # disabling authenticated links for now a = customer.account return nil unless a.present? UrlHelper.instance.my_order_path(self) # if self.customer.account.auth_token_required? # path = self.customer.account.append_token(path, persist=true) # end end |
#public_payment_link ⇒ Object
4430 4431 4432 |
# File 'app/models/order.rb', line 4430 def public_payment_link "https://#{WEB_HOSTNAME}/pay-order/#{encrypted_id}" end |
#purchase_early_label_if_requested ⇒ Hash?
Purchase shipping label early if requested and order is eligible
Called from after_transition to awaiting_deliveries
Stores result in early_label_purchase_result for controller to display flash messages
Logs all API calls to early_label_api_log for debugging/visibility
Sends EDI admin notification on any failure
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 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 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 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 |
# File 'app/models/order.rb', line 2930 def purchase_early_label_if_requested # Initialize API log for this purchase attempt @early_label_api_log = [] log_early_label_event('start', "Checking early label for order #{reference_number}") unless purchase_label_early? log_early_label_event('skip', 'purchase_label_early flag is false') Rails.logger.debug { "[EarlyLabel] Skipping - purchase_label_early is false for order #{reference_number}" } return end unless marketplace_early_label_eligible? error_msg = "Order not eligible for marketplace early label (partner: #{edi_orchestrator_partner})" log_early_label_event('skip', error_msg) Rails.logger.info("[EarlyLabel] Skipping - #{error_msg}") self.early_label_purchase_result = { success: false, error: 'Order is not eligible for marketplace early label purchase' } return early_label_purchase_result end unless early_label_purchase_enabled_for_partner? log_early_label_event('skip', 'early_label_purchase_disabled on orchestrator') Rails.logger.info("[EarlyLabel] Skipping - early label purchase disabled for partner #{edi_orchestrator_partner}") return end if has_early_purchased_label? log_early_label_event('skip', "Already has early label: #{early_label_tracking_number}") Rails.logger.info("[EarlyLabel] Skipping - order #{reference_number} already has early label: #{early_label_tracking_number}") self.early_label_purchase_result = { success: true, tracking_number: early_label_tracking_number, carrier: early_label_carrier, already_purchased: true } return early_label_purchase_result end delivery = deliveries.first selected_sc = delivery&.selected_shipping_cost unless selected_shipping_cost_supports_early_label?(selected_sc, delivery) carrier_desc = selected_sc&.shipping_option&.name || 'none' log_early_label_event('skip', "Selected shipping cost (#{carrier_desc}) does not support early label — unchecking flag and skipping") Rails.logger.info("[EarlyLabel] Skipping — selected shipping cost '#{carrier_desc}' is not a marketplace rate for order #{reference_number}, clearing purchase_label_early") update_column(:purchase_label_early, false) return end log_early_label_event('begin_purchase', "Starting early label purchase for order #{reference_number}") Rails.logger.info("[EarlyLabel] Purchasing early label for order #{reference_number}") unless delivery&.supported_shipping_carrier? error_msg = "Delivery #{delivery&.id} does not have supported shipping carrier (carrier: #{delivery&.carrier})" log_early_label_event('error', error_msg) early_label_failure_notification(error_msg) Rails.logger.warn("[EarlyLabel] Skipping - #{error_msg}") self.early_label_purchase_result = { success: false, error: "Delivery does not have a supported shipping carrier (#{delivery&.carrier})" } save_early_label_api_log return early_label_purchase_result end begin is_amazon = edi_orchestrator_partner&.start_with?('amazon_seller') # Check for existing shipments - if none exist, auto-create suggested shipments # This is necessary because Walmart EDI orders don't have shipments until warehouse processing existing_shipments = delivery.shipments.non_voided.to_a if existing_shipments.empty? log_early_label_event('auto_create_shipments', "No shipments exist for delivery #{delivery.id}, creating suggested shipments") Rails.logger.info("[EarlyLabel] No shipments exist for delivery #{delivery.id}, auto-creating suggested shipments") Shipping::CreateSuggestedShipment.new.process(delivery) delivery.reload existing_shipments = delivery.shipments.non_voided.to_a if existing_shipments.empty? error_msg = "Failed to create suggested shipments for delivery #{delivery.id} - no packaging data available" log_early_label_event('error', error_msg) early_label_failure_notification(error_msg) Rails.logger.error("[EarlyLabel] Cannot purchase early label - #{error_msg}") self.early_label_purchase_result = { success: false, error: 'Could NOT purchase ship labels early: failed to create suggested shipments (no packaging data available)' } save_early_label_api_log return early_label_purchase_result end log_early_label_event('shipments_created', "Auto-created #{existing_shipments.size} suggested shipment(s)") Rails.logger.info("[EarlyLabel] Auto-created #{existing_shipments.size} suggested shipment(s) for early label purchase") end log_early_label_event('shipments_ready', "Using #{existing_shipments.size} shipment(s) for label purchase") Rails.logger.info("[EarlyLabel] Using #{existing_shipments.size} shipment(s) for label purchase") if is_amazon && existing_shipments.many? log_early_label_event('skip_multi_package', "Amazon early label purchase does not yet support multiple shipments (#{existing_shipments.size}). Labels will be purchased at ship-label time.") Rails.logger.info("[EarlyLabel] Skipping early label for multi-package Amazon order #{reference_number} (#{existing_shipments.size} shipments) — warehouse will purchase labels at ship-label time") save_early_label_api_log return nil end shipper = build_early_label_shipper(delivery, existing_shipments) log_early_label_event('api_call', 'Calling find_rates API', { po_number: edi_po_number }) rates_response = shipper.find_rates merge_shipper_api_log(shipper) log_early_label_event('api_response', 'find_rates response', { success: rates_response[:success], rates_count: rates_response[:rates]&.length || 0, message: rates_response[:message] }) unless rates_response[:success] && rates_response[:rates].any? error_msg = "Failed to get shipping rates: #{rates_response[:message]}" log_early_label_event('error', error_msg) early_label_failure_notification(error_msg) Rails.logger.error("[EarlyLabel] Failed to get rates for order #{reference_number}: #{rates_response[:message]}") self.early_label_purchase_result = { success: false, error: error_msg } save_early_label_api_log return early_label_purchase_result end rate = match_early_label_rate(delivery, rates_response[:rates], is_amazon:) unless rate so_name = delivery.selected_shipping_cost&.shipping_option&.name || 'unknown' error_msg = "User-selected rate '#{so_name}' is no longer available in fresh marketplace rates — holding order for re-selection" log_early_label_event('hold', error_msg, { selected_shipping_cost_id: delivery.selected_shipping_cost_id, selected_option: so_name, available_rates_count: rates_response[:rates]&.length || 0 }) Rails.logger.warn("[EarlyLabel] #{error_msg} for order #{reference_number}") hold_for_early_label_mismatch!(delivery, error_msg) save_early_label_api_log self.early_label_purchase_result = { success: false, error: error_msg, held: true } return early_label_purchase_result end label_rate = if is_amazon rate.merge( request_token: rate[:amz_request_token], rate_id: rate[:amz_rate_id], carrier_id: rate[:amz_carrier_id], carrier_name: rate[:amz_carrier_name], service_name: rate[:amz_service_name] ) else box_items = build_early_label_box_items(delivery) rate.merge( carrier_id: rate[:sww_carrier_id], service_type: rate[:sww_service_type], box_items: box_items ) end log_early_label_event('api_call', 'Calling create_label API', { po_number: edi_po_number, carrier: label_rate[:carrier_id] || label_rate[:amz_carrier_id], service: label_rate[:service_type] || label_rate[:amz_service_name] }) label_result = shipper.create_label(label_rate) merge_shipper_api_log(shipper) log_early_label_event('api_response', 'create_label response', { success: label_result[:success], tracking_number: label_result[:tracking_number], carrier: label_result[:carrier], error: label_result[:error] }) unless label_result[:success] error_msg = "Failed to create shipping label: #{label_result[:error]}" log_early_label_event('error', error_msg) early_label_failure_notification(error_msg) Rails.logger.error("[EarlyLabel] Failed to create label for order #{reference_number}: #{label_result[:error]}") self.early_label_purchase_result = { success: false, error: error_msg } save_early_label_api_log return early_label_purchase_result end # SAFEGUARD A: Atomic label purchase - PDF must be saved before marking complete # Download and store the label PDF BEFORE updating metadata # This prevents the race condition where void happens before PDF is downloaded marketplace = is_amazon ? 'Amazon' : 'Walmart' log_early_label_event('pdf_download', 'Downloading and storing label PDF', { tracking_number: label_result[:tracking_number], carrier: label_result[:carrier] }) pdf_upload = download_and_store_early_label_pdf_atomic(shipper, label_result, is_amazon:) unless pdf_upload error_msg = "Label was created but PDF could not be downloaded/stored. The label exists in #{marketplace} but is not recoverable locally." log_early_label_event('error', error_msg, { tracking_number: label_result[:tracking_number], carrier: label_result[:carrier], partial_failure: true }) early_label_failure_notification("#{error_msg} Tracking: #{label_result[:tracking_number]}") Rails.logger.error("[EarlyLabel] PDF storage failed for order #{reference_number} - marking purchase as failed") self.early_label_purchase_result = { success: false, error: "Label was created but PDF could not be stored. The label exists in #{marketplace} but is not recoverable locally. Please void and recreate.", tracking_number: label_result[:tracking_number], carrier: label_result[:carrier], partial_failure: true } save_early_label_api_log return early_label_purchase_result end log_early_label_event('pdf_stored', 'Label PDF successfully stored', { upload_id: pdf_upload.id }) # ONLY update metadata after PDF is confirmed saved update!( early_label_tracking_number: label_result[:tracking_number], early_label_carrier: label_result[:carrier], early_label_sww_label_id: is_amazon ? label_result[:shipment_id] : label_result[:label_id], early_label_service_type: is_amazon ? label_result[:service_name] : label_result[:service_type], early_label_purchased_at: Time.current, early_label_delivery_id: delivery.id, early_label_voided_at: nil, early_label_void_reason: nil, early_label_shipments_count: existing_shipments.size, early_label_shipments_data: build_early_label_shipments_data(existing_shipments) ) if label_result[:additional_labels].present? additional_packages = label_result[:additional_labels].map do |l| { shipment_id: l[:shipment_id], tracking_number: l[:tracking_number] } end = || {} ['additional_packages'] = additional_packages update_column(:early_label_metadata, ) end log_early_label_event('success', 'Early label purchase completed', { tracking_number: label_result[:tracking_number], carrier: label_result[:carrier], service_type: label_result[:service_type] }) Rails.logger.info("[EarlyLabel] Successfully purchased early label for order #{reference_number}: tracking=#{label_result[:tracking_number]}") log_early_label_event('edi_confirm', "Sending EDI ship confirm to #{is_amazon ? 'Amazon' : 'Walmart'}") fire_early_label_edi_confirm(delivery, label_result) save_early_label_api_log self.early_label_purchase_result = { success: true, tracking_number: label_result[:tracking_number], carrier: label_result[:carrier] } early_label_purchase_result rescue StandardError => e error_msg = "Exception during early label purchase: #{e.}" log_early_label_event('exception', error_msg, { backtrace: e.backtrace.first(3) }) early_label_failure_notification(error_msg) save_early_label_api_log Rails.logger.error("[EarlyLabel] Error purchasing early label for order #{reference_number}: #{e.}") Rails.logger.error(e.backtrace.first(5).join("\n")) self.early_label_purchase_result = { success: false, error: e. } early_label_purchase_result end end |
#purchase_order ⇒ PurchaseOrder
258 |
# File 'app/models/order.rb', line 258 belongs_to :purchase_order, optional: true |
#quote ⇒ Quote
250 |
# File 'app/models/order.rb', line 250 belongs_to :quote, inverse_of: :orders, optional: true |
#quotes ⇒ Object
1505 1506 1507 1508 1509 1510 |
# File 'app/models/order.rb', line 1505 def quotes qs = [] qs << quote qs += room_configurations.map { |rc| rc.quotes.completed_quotes.last } qs.uniq.compact end |
#ready_for_pending_payment? ⇒ Boolean
2021 2022 2023 |
# File 'app/models/order.rb', line 2021 def ready_for_pending_payment? ready_for_shipping? end |
#ready_for_service? ⇒ Boolean
2057 2058 2059 |
# File 'app/models/order.rb', line 2057 def ready_for_service? belongs_to_smartservice_group? end |
#ready_for_shipping? ⇒ Boolean
2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 |
# File 'app/models/order.rb', line 2061 def ready_for_shipping? if shipping_address.nil? return false end # or self.chosen_shipping_method.blank? if has_unreserved_line_items? errors.add :base, "One or more line items require a serial number reservation. Line items: #{line_items.select { |li| li.require_reservation? && !li.fully_reserved? }.collect(&:sku).join(', ')}" return false end if has_committed_serial_number_reservations? errors.add :base, 'Not all reserved serial numbers are available' return false end if is_sales_order? && price_match && price_match != line_total errors.add :base, "EDI order price match discrepancy, expects line total to be #{price_match}" return false end if is_sales_order? && customer&.customer_record&.always_custom_packing_list? && edi_is_pick_slip_required? && uploads.in_category('custom_packing_slip_pdf').blank? houzz_snippet = String.new if customer&.is_houzz? && edi_po_number.present? houzz_snippet = ", get it here: <a href='https://www.houzz.com//printBuyerOrder//orderId=#{edi_po_number}' target=_new><i text='https://www.houzz.com//printBuyerOrder//orderId=#{edi_po_number}' class='fas fa-arrow-up-right-from-square'></i> https://www.houzz.com//printBuyerOrder//orderId=#{edi_po_number}</a>" end errors.add :base, "Customer requires a custom packing slip pdf for all orders#{houzz_snippet}".html_safe return false end # enforce that all Canadian Tire orders must ship to a store and use the store name format if is_sales_order? && customer&.billing_entity&.is_canadian_tire? && !!((shipping_address.company_name || shipping_address.person_name) =~ CustomerConstants::CANADIAN_TIRE_STORE_NAME_REGEX) != true errors.add :base, CustomerConstants::CANADIAN_TIRE_STORE_NAME_ERROR_MESSAGE return false end unless shipping_address_valid_and_not_po_box_if_present errors.add :base, 'Shipping address invalid' return false end if deliveries.any? { |d| d.incurs_oversized_penalty? } errors.add :base, 'Shipping packages exceed oversized limits or include a pallet and would incur big penalties, please review packaging and consider shipping via LTL freight.' return false end # if shipping_address.is_warehouse # errors.add :base, 'Warehouse pickup, manager must release from management hold' # return false # end if shipping_address.is_warehouse || (order_type == Order::STORE_TRANSFER) || !%w[USA CAN].include?(shipping_address.country_iso3) || shipping_address.verified_for_shipping || shipping_address.disable_address_correction? || shipping_address.override_all_address_validation? return true end shipping_address.require_carrier_validation = true carrier if shipping_address.country_iso3 == 'CAN' 'Purolator' end # Canadian addresses can be really skitchy so only use Purolator's city/postal code validation shipping_address.validate_with_carrier = carrier shipping_address.do_not_accept_legacy_verified = true res = shipping_address.external_validation_with_carrier(true) unless res || is_www || errors.to_a.any? { |e| e.index('Customer must be contacted to obtain a valid shipping address') } # don't show this validation error for www errors.add :base, "Order carrier validation#{if carrier.present? " with #{carrier}" end} must pass for order to be released. Carrier validation can't be skipped by disabling address correction, but in exceptional circumstances can be skipped by a system administrator. In worst cases, customer may need to be contacted to obtain a valid shipping address. " end res end |
#ready_for_warehouse? ⇒ Boolean
2017 2018 2019 |
# File 'app/models/order.rb', line 2017 def ready_for_warehouse? all_items_in_stock && ready_for_shipping? end |
#recipient_name ⇒ Object
1063 1064 1065 |
# File 'app/models/order.rb', line 1063 def recipient_name shipping_address&.company_name || shipping_address&.person_name || customer&.full_name end |
#related_activities ⇒ Object
1701 1702 1703 |
# File 'app/models/order.rb', line 1701 def Activity.where(id: ) end |
#related_activities_ids ⇒ Object
1697 1698 1699 |
# File 'app/models/order.rb', line 1697 def [activities.pluck(:id), delivery_activities.pluck(:id)].flatten end |
#release_order_or_hold ⇒ Object
1721 1722 1723 |
# File 'app/models/order.rb', line 1721 def release_order_or_hold release_order unless accounting_hold_order? end |
#remove_room_configuration(rc) ⇒ Object
1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 |
# File 'app/models/order.rb', line 1512 def remove_room_configuration(rc) order_label = cart? ? 'cart' : 'order' if rc && room_configuration_ids.include?(rc.id) room_configurations.delete rc if opportunity_id == rc.opportunity_id update(opportunity: opportunities[1]) # this will be nil if no room_configurations, as desired. end if rc.quote_ids.include?(quote_id) update(quote: quotes[1]) # this will be nil if no room_configurations, as desired. end reset_discount(reset_item_pricing: false) res = save msg = if res "Room/Heated Space #{rc.name} has been removed from #{order_label}." else "Room/Heated Space #{rc.name} has been removed from #{order_label}." end else res = false msg = "Room/Heated Space #{rc.name} is not in the #{order_label}." end { status: res, message: msg } end |
#require_cc_for_advance_credit? ⇒ Boolean
1140 1141 1142 1143 1144 1145 1146 1147 1148 |
# File 'app/models/order.rb', line 1140 def require_cc_for_advance_credit? if allow_advance_credit_without_cc? false elsif rma && rma.rma_items.active.all?(&:will_not_be_returned?) false else customer.credit_card_vaults.valid.visible.empty? && rma.rma_items.any?(&:is_customer_fault?) end end |
#requires_intervention? ⇒ Boolean
1761 1762 1763 |
# File 'app/models/order.rb', line 1761 def requires_intervention? crm_back_order? || in_cr_hold? || pending_review? || || pending_payment? end |
#reset_cart ⇒ Object
2352 2353 2354 2355 2356 2357 2358 2359 |
# File 'app/models/order.rb', line 2352 def reset_cart line_items.destroy_all deliveries.quoting.destroy_all discounts.destroy_all self.shipping_address = nil save prune_cart_rooms end |
#restricted_order_type? ⇒ Boolean
1332 1333 1334 |
# File 'app/models/order.rb', line 1332 def restricted_order_type? RESTRICTED_ORDER_TYPES.keys.include?(order_type) end |
#reviewer ⇒ Object
Returns the reviewer info for Reviews.io invitation
Prefers contact over customer if present
1069 1070 1071 1072 1073 1074 1075 |
# File 'app/models/order.rb', line 1069 def reviewer reviewer_party = contact.presence || customer { email: reviewer_party&.email, name: reviewer_party&.full_name } end |
#rma ⇒ Rma
253 |
# File 'app/models/order.rb', line 253 belongs_to :rma, inverse_of: :orders, optional: true |
#rma_cancel ⇒ Object
2826 2827 2828 2829 2830 2831 |
# File 'app/models/order.rb', line 2826 def rma_cancel Order.transaction do deliveries.active.each(&:destroy) destroy end end |
#rma_items ⇒ ActiveRecord::Relation<RmaItem>
281 |
# File 'app/models/order.rb', line 281 has_many :rma_items, inverse_of: :replacement_order |
#rmas ⇒ ActiveRecord::Relation<Rma>
277 |
# File 'app/models/order.rb', line 277 has_many :rmas, foreign_key: :original_order_id |
#sales_support_rep ⇒ Employee
248 |
# File 'app/models/order.rb', line 248 belongs_to :sales_support_rep, class_name: 'Employee', optional: true |
#save_early_label_api_log ⇒ Object
Save the accumulated API log to the order's early_label_metadata
Called at the end of purchase_early_label_if_requested (success or failure)
3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 |
# File 'app/models/order.rb', line 3379 def save_early_label_api_log return unless @early_label_api_log.present? # Append to existing log if present, or create new existing_log = early_label_api_log || [] new_log = existing_log + @early_label_api_log # Keep only the last 50 entries to prevent unbounded growth new_log = new_log.last(50) if new_log.length > 50 # Ensure early_label_metadata is a hash before merging = || {} update_column(:early_label_metadata, .merge('api_log' => new_log)) @early_label_api_log = nil rescue StandardError => e Rails.logger.error("[EarlyLabel] Failed to save API log: #{e.}") Rails.logger.error(e.backtrace.first(3).join("\n")) end |
#schedule_follow_up_activity ⇒ Object
2261 2262 2263 2264 |
# File 'app/models/order.rb', line 2261 def schedule_follow_up_activity result = Order::FollowUpScheduler.new.process(self) result.activity_scheduled? end |
#search_text ⇒ Object (protected)
4613 4614 4615 4616 4617 4618 4619 4620 4621 |
# File 'app/models/order.rb', line 4613 def search_text return nil if cart? st = [] st << reference_number st += payments.pluck(:po_number) st << rma_reference st.join(' ') end |
#secondary_sales_rep ⇒ Object
1400 1401 1402 1403 1404 |
# File 'app/models/order.rb', line 1400 def secondary_sales_rep return invoiced_secondary_sales_rep if order_closed? invoiced_secondary_sales_rep || customer&.secondary_sales_rep end |
#selected_shipping_cost_supports_early_label?(selected_sc, delivery) ⇒ Boolean
Checks whether the delivery's selected shipping cost is compatible with
the marketplace early-label flow. Today this means AMZBS or SWW; long-term
it may also include Heatwave native ship-labels.
3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 |
# File 'app/models/order.rb', line 3543 def selected_shipping_cost_supports_early_label?(selected_sc, delivery) return false unless delivery.present? && selected_sc.present? is_amazon = edi_orchestrator_partner&.start_with?('amazon_seller') is_walmart = edi_orchestrator_partner&.start_with?('walmart_seller') if is_amazon selected_sc.is_amzbs? elsif is_walmart selected_sc.is_sww? else false end end |
#selection_name ⇒ Object
2822 2823 2824 |
# File 'app/models/order.rb', line 2822 def selection_name "#{reference_number} #{customer.full_name} (#{shipped_date.present? ? shipped_date.to_fs(:crm_default) : 'not shipped'})" end |
#send_back_order_notification ⇒ Object
2521 2522 2523 |
# File 'app/models/order.rb', line 2521 def send_back_order_notification InternalMailer.back_order_notification(self).deliver_later end |
#send_early_label_void_alert(reason:, details:) ⇒ Object
Send alert email when early label void fails or is blocked
SAFEGUARD C: Ensures operations team is notified of label issues
3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 |
# File 'app/models/order.rb', line 3286 def send_early_label_void_alert(reason:, details:) marketplace = edi_orchestrator_partner&.start_with?('amazon_seller') ? 'Amazon' : 'Walmart' portal = marketplace == 'Amazon' ? 'Amazon Seller Central' : 'Walmart Seller Portal' Rails.logger.warn("[EarlyLabel] Sending void alert for order #{reference_number}: #{reason}") InternalMailer.generic_mailer( subject: "[URGENT] Early Label Void Issue - Order #{reference_number}", body: <<~BODY, An early label void issue occurred that requires attention. ORDER DETAILS: - Order: #{reference_number} - Tracking: #{early_label_tracking_number} - Carrier: #{early_label_carrier} - Label Purchased: #{early_label_purchased_at&.strftime('%Y-%m-%d %H:%M:%S %Z')} - EDI Partner: #{edi_orchestrator_partner} - PO Number: #{edi_po_number} - Marketplace: #{marketplace} ISSUE: - Reason: #{reason} - Details: #{details} ACTION REQUIRED: 1. Check the order in Heatwave: https://#{CRM_HOSTNAME}/en-US/orders/#{id} 2. Check the label status in #{portal} 3. If the label exists in #{marketplace} but not in Heatwave, manually void it in #{portal} 4. Update the order status as needed This is an automated alert from the Early Label Purchase system. BODY to: ORDERS_EMAIL, from: ADMINISTRATOR_EMAIL ).deliver_later rescue StandardError => e Rails.logger.error("[EarlyLabel] Failed to send void alert email: #{e.}") end |
#send_online_order_confirmation ⇒ Object
2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 |
# File 'app/models/order.rb', line 2541 def send_online_order_confirmation if email_for_order_confirmation sender = customer.primary_sales_rep co = CommunicationBuilder.new( resource: self, sender_party: sender, sender: sender || INFO_EMAIL, recipient_party: party_for_order_confirmation, emails: email_for_order_confirmation, recipient_name: party_for_order_confirmation.name, template_system_code: 'ONLINE_ORDER_CONFIRM', bcc: sender&.email, merge_options: { order: to_liquid } ).create if co.draft? { status_code: :error, status_message: co.errors_to_s } else # IMPORTANT!!! THIS MUST BE SET HERE update_column(:tracking_email, [email_for_order_confirmation]) { status_code: :ok, status_message: "Order confirmation e-mail sent to #{party_for_order_confirmation.name} #{email_for_order_confirmation}." } end else { status_code: :error, status_message: "Can't send: order/customer e-mail is blank!" } end end |
#send_payment_automatically_authorized_notification ⇒ Object
2537 2538 2539 |
# File 'app/models/order.rb', line 2537 def InternalMailer.(self).deliver_later end |
#send_profit_review_notification ⇒ Object
2529 2530 2531 |
# File 'app/models/order.rb', line 2529 def send_profit_review_notification InternalMailer.order_profit_review_notification(self).deliver_later end |
#send_ready_for_pickup_email ⇒ Object
4481 4482 4483 |
# File 'app/models/order.rb', line 4481 def send_ready_for_pickup_email send_tracking_template_email 'ORDER_PICKUP' end |
#send_release_authorization_notification ⇒ Object
2533 2534 2535 |
# File 'app/models/order.rb', line 2533 def InternalMailer.(self).deliver_later end |
#send_request_carrier_assignment_notification ⇒ Object
2525 2526 2527 |
# File 'app/models/order.rb', line 2525 def send_request_carrier_assignment_notification InternalMailer.request_carrier_assignment_notification(self).deliver_later end |
#send_tracking_email ⇒ Object
4477 4478 4479 |
# File 'app/models/order.rb', line 4477 def send_tracking_email send_tracking_template_email 'ORDER_TRACKING' end |
#send_tracking_template_email(template_code) ⇒ Object
4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 |
# File 'app/models/order.rb', line 4485 def send_tracking_template_email(template_code) return { status_code: :error, status_message: "Can't send #{template_code}: no tracking e-mail present!" } unless tracking_email.present? sender = customer.primary_sales_rep co = CommunicationBuilder.new(resource: self, sender_party: sender, sender: (sender.nil? ? INFO_EMAIL : nil), recipient_party: customer, emails: tracking_email.join(','), template_system_code: template_code).create if co.draft? { status_code: :error, status_message: co.errors_to_s } else { status_code: :ok, status_message: "#{template_code} e-mail sent." } end end |
#service_only_order? ⇒ Boolean
1741 1742 1743 |
# File 'app/models/order.rb', line 1741 def service_only_order? line_items.non_shipping.any? && line_items.non_shipping.all?(&:is_service?) end |
#set_currency ⇒ Object (protected)
4587 4588 4589 |
# File 'app/models/order.rb', line 4587 def set_currency self.currency ||= customer&.store&.currency end |
#set_custom_order_agreement ⇒ Object
Checks for the presence of custom products in excess of $2k
4356 4357 4358 4359 4360 4361 4362 |
# File 'app/models/order.rb', line 4356 def set_custom_order_agreement return unless is_sales_order? return if custom_order_agreement_bypass? return unless meets_custom_products_threshold? update_column(:custom_order_agreement_status, Order.custom_order_agreement_statuses['custom_order_agreement_required']) end |
#set_default_tracking_email(force: false) ⇒ Object
2791 2792 2793 2794 2795 |
# File 'app/models/order.rb', line 2791 def set_default_tracking_email(force: false) return unless force || tracking_email.empty? self.tracking_email = Order::DefaultTrackingEmailExtractor.new(self).emails end |
#set_min_profit_markup ⇒ Object
2783 2784 2785 2786 2787 2788 2789 |
# File 'app/models/order.rb', line 2783 def set_min_profit_markup self.min_profit_markup = if is_sales_order? default_sales_markup else 0 end end |
#set_opportunity_source ⇒ Object
1372 1373 1374 1375 1376 1377 1378 1379 |
# File 'app/models/order.rb', line 1372 def set_opportunity_source return unless source && saved_change_to_source_id? return if source&.unknown_source? return unless opportunity # If you made it this far, then you can update the opportunity source opportunity.update_column(:source_id, source.id) end |
#set_shipped_date ⇒ Object
2275 2276 2277 |
# File 'app/models/order.rb', line 2275 def set_shipped_date update_attribute(:shipped_date, Date.current) unless shipped_date.present? end |
#ship_from_attributes(delivery = nil) ⇒ Object
2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 |
# File 'app/models/order.rb', line 2490 def ship_from_attributes(delivery = nil) res = {} # Here we use delivery, if present, to extract the origin address for the ship from, as well as the contact info. This is for drop shippers, but should also cover when shipper is WY US or Canada res[:address] = delivery&.origin_address || store.warehouse_address # sort of a kludge but for now use shipping configuration's sender phone for legacy matching res[:phone] = delivery&.origin_address&.party&.phone || SHIPPING_SHIPPER_CONFIGURATION[country.iso3.to_sym][:shipper_phone] res[:email] = delivery&.origin_address&.party&.email || SHIPPING_SHIPPER_CONFIGURATION[country.iso3.to_sym][:shipper_email] # post rb_any_ship_from, we actually want the ship_from to be the physical ship from and handle the shipper separately if bill_shipping_to_customer && delivery && delivery.chosen_shipping_method && delivery.chosen_shipping_method.shipping_account_number if delivery.chosen_shipping_method.shipping_account_number.address # here, we implement override of ship from address to be the address linked in the shipping_account_number, if any. res[:address] = delivery.chosen_shipping_method.shipping_account_number.address end if begin delivery.chosen_shipping_method.shipping_account_number.phone rescue StandardError false end # here, we implement override of ship from phone to be the phone linked in the shipping_account_number, if any. res[:phone] = delivery.chosen_shipping_method.shipping_account_number.phone.detail end end # attention_name cannot be blank!!!! res[:attention_name] = res[:address].person_name res[:attention_name] = 'Shipping Department' unless res[:attention_name].present? res[:name] = (res[:address].company_name || res[:address].person_name) res[:phone] = res[:phone].scan(/[0-9]/).join('') if res[:phone] res end |
#ship_to_attributes ⇒ Object
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 |
# File 'app/models/order.rb', line 2416 def ship_to_attributes res = {} res[:address] = shipping_address # attention_name cannot be blank!!!! res[:attention_name] = attention_name res[:attention_name] = res[:address].person_name unless res[:attention_name].present? res[:attention_name] = 'Customer' unless res[:attention_name].present? if res[:address].company_name res[:name] = res[:address].company_name res[:name] = 'Customer' unless res[:name].present? else res[:name] = attention_name res[:name] = res[:address].person_name unless res[:name].present? res[:name] = 'Customer' unless res[:name].present? end res[:phone] = shipping_phone.presence || customer.phone || customer.cell_phone || customer.primary_sales_rep&.direct_phone || customer.store.contact_number || SHIPPING_SHIPPER_CONFIGURATION.dig(country.iso3.to_sym, :shipper_phone) res[:email] = first_tracking_email&.strip&.presence || customer.primary_sales_rep&.email || customer.store.contact_email || SHIPPING_SHIPPER_CONFIGURATION.dig(country.iso3.to_sym, :shipper_email) # need something here res end |
#ship_weight ⇒ Object
4457 4458 4459 |
# File 'app/models/order.rb', line 4457 def ship_weight @ship_weight ||= [0.1, line_items.includes(:item).non_shipping.parents_only.map(&:total_shipping_weight).sum.round(1)].max end |
#shipments ⇒ ActiveRecord::Relation<Shipment>
291 |
# File 'app/models/order.rb', line 291 has_many :shipments, -> { order(:id) }, through: :deliveries |
#shipping_account_number ⇒ ShippingAccountNumber
DELIVERY REFACTOR HERE
257 |
# File 'app/models/order.rb', line 257 belongs_to :shipping_account_number, optional: true |
#shipping_address ⇒ Address
266 |
# File 'app/models/order.rb', line 266 belongs_to :shipping_address, class_name: 'Address', validate: true, optional: true |
#shipping_address_valid_and_not_po_box_if_present(rate_shopping = false) ⇒ Object
2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 |
# File 'app/models/order.rb', line 2000 def shipping_address_valid_and_not_po_box_if_present(rate_shopping = false) res = false if shipping_address.nil? errors[:shipping_address] << 'must be selected' elsif shipping_address.present? carrer_to_use = carrier carrer_to_use = nil if rate_shopping if shipping_address.valid? && shipping_address.not_a_po_box?(carrer_to_use) res = true else res = false errors[:shipping_address] << shipping_address.errors. end end res end |
#shipping_cutoff_advance_order? ⇒ Boolean
1600 1601 1602 |
# File 'app/models/order.rb', line 1600 def shipping_cutoff_advance_order? line_items.goods.parents_only.any? { |a| !a.in_stock? } end |
#shipping_cutoff_next_day? ⇒ Boolean
1596 1597 1598 |
# File 'app/models/order.rb', line 1596 def shipping_cutoff_next_day? line_items.goods.parents_only.any? { |a| a.in_stock? && a.ships_via_freight? } # next day end |
#shipping_cutoff_same_day? ⇒ Boolean
1592 1593 1594 |
# File 'app/models/order.rb', line 1592 def shipping_cutoff_same_day? line_items.goods.parents_only.all? { |a| a.in_stock? && !a.ships_via_freight? } # same day end |
#shipping_date_warnings ⇒ Object
Warnings about shipping dates that don't block order release
These are displayed to users but don't prevent state transitions
Note: requested_ship_before is automatically advanced when the user revisits the shipping form,
so no warning is needed when it's in the past.
1845 1846 1847 |
# File 'app/models/order.rb', line 1845 def shipping_date_warnings [] end |
#should_commit_stock? ⇒ Boolean
1015 1016 1017 |
# File 'app/models/order.rb', line 1015 def should_commit_stock? (future_release_date.present? && !do_not_reserve_stock?) || all_funds_available? end |
#smartservice_ticket ⇒ Object
2049 2050 2051 |
# File 'app/models/order.rb', line 2049 def smartservice_ticket support_cases&.services&.pending_service_payment&.first end |
#sms_enabled_numbers ⇒ Object
1132 1133 1134 |
# File 'app/models/order.rb', line 1132 def sms_enabled_numbers ContactPoint.where(party_id: all_participant_ids).sms_numbers.order(:detail).map(&:formatted_for_sms).uniq end |
#sms_messages ⇒ Object
1136 1137 1138 |
# File 'app/models/order.rb', line 1136 def SmsMessage.for_numbers(sms_enabled_numbers) end |
#sold_to_billing_address ⇒ Address
267 |
# File 'app/models/order.rb', line 267 belongs_to :sold_to_billing_address, class_name: 'Address', foreign_key: :sold_to_billing_address, optional: true |
#spiff_enrollment ⇒ SpiffEnrollment
255 |
# File 'app/models/order.rb', line 255 belongs_to :spiff_enrollment, optional: true |
#spiff_rep ⇒ Contact
254 |
# File 'app/models/order.rb', line 254 belongs_to :spiff_rep, class_name: 'Contact', optional: true, inverse_of: :spiff_orders |
#spiff_reward ⇒ Object
1473 1474 1475 |
# File 'app/models/order.rb', line 1473 def spiff_reward '%.2f' % spiff_enrollment.spiff.eligible_reward(self) end |
#state_description(describe_state = nil) ⇒ Object
1412 1413 1414 |
# File 'app/models/order.rb', line 1412 def state_description(describe_state = nil) OrderConstants::STATE_DESCRIPTION[describe_state || state] end |
#state_list ⇒ Object
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 |
# File 'app/models/order.rb', line 1426 def state_list if order_type == CREDIT_ORDER transition %i[cart pending pending_payment pending_release_authorization crm_back_order awaiting_deliveries in_cr_hold created awaiting_return] => :fraudulent, if: :can_be_cancelled? %i[created awaiting_return pending_review ready_for_printing printed cancelled] else states = [] states << :cart if cart? states << :pending states << :pending_payment states << :crm_back_order if crm_back_order? states << :pending_release_authorization if states << :in_cr_hold if in_cr_hold? states << :needs_serial_number_reservation states << :awaiting_deliveries states << :processing_deliveries states << :partially_invoiced states << :invoiced if begin cancelled? rescue StandardError false end states << :cancelled end if begin fraudulent? rescue StandardError false end states << :fraudulent end states end end |
#stock_status ⇒ Object
1996 1997 1998 |
# File 'app/models/order.rb', line 1996 def stock_status LineItem.inventory_check(self) end |
#stop_for_pre_pack? ⇒ Boolean
4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 |
# File 'app/models/order.rb', line 4371 def stop_for_pre_pack? return false unless can_request_estimated_packaging? return false if customer.is_e_commerce_misc? return false if room_configurations.present? && !room_configurations.all?(&:transmittable_and_settled?) return false if deliveries.all? { |d| d.shipments.packed.any? && d.all_lines_allocated_to_shipments? } res = need_to_pre_pack_reasons return res if res&.any? false end |
#stop_for_profit_review? ⇒ Boolean
4364 4365 4366 4367 4368 4369 |
# File 'app/models/order.rb', line 4364 def stop_for_profit_review? # Etailer orders are immune return false if customer.is_e_commerce_misc? !profit_margins_met? end |
#store ⇒ Object
1011 1012 1013 |
# File 'app/models/order.rb', line 1011 def store from_store || customer&.store end |
#store_additional_early_label_pdfs(additional_labels) ⇒ Object
4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 |
# File 'app/models/order.rb', line 4038 def store_additional_early_label_pdfs(additional_labels) additional_labels.each_with_index do |label, idx| next unless label[:label_data].present? && label[:tracking_number].present? store_early_label_pdf(label[:label_data], label[:tracking_number]) Rails.logger.info("[EarlyLabel] Stored additional package #{idx + 2} label PDF (tracking: #{label[:tracking_number]})") rescue StandardError => e Rails.logger.warn("[EarlyLabel] Failed to store additional package #{idx + 2} label PDF: #{e.}") end end |
#store_amazon_label_pdf_atomic(label_result, tracking_number, marketplace) ⇒ Object
Amazon: label data is returned inline — store it and verify persistence.
For multi-package orders, also stores additional package labels.
3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 |
# File 'app/models/order.rb', line 3943 def store_amazon_label_pdf_atomic(label_result, tracking_number, marketplace) label_data = label_result[:label_data] unless label_data.present? Rails.logger.warn('[EarlyLabel] Amazon label data not returned inline; label PDF will be attached at ship-label time') send_early_label_void_alert( reason: 'Amazon label data missing from purchase response', details: "Label was purchased but no inline label data was returned. " \ "The label exists in #{marketplace}'s system but PDF is not stored locally." ) return nil end upload = store_early_label_pdf(label_data, tracking_number) if upload&.persisted? Rails.logger.info("[EarlyLabel] Amazon label PDF stored as upload #{upload.id}") store_additional_early_label_pdfs(label_result[:additional_labels]) if label_result[:additional_labels].present? return upload end Rails.logger.error("[EarlyLabel] Amazon label PDF storage failed for order #{reference_number}") send_early_label_void_alert( reason: 'Amazon label PDF storage failed', details: "Label data was returned but could not be persisted. " \ "The label exists in #{marketplace}'s system but is not stored locally." ) nil end |
#store_early_label_pdf(label_data, tracking_number) ⇒ Upload?
Store early label PDF on the order
4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 |
# File 'app/models/order.rb', line 4017 def store_early_label_pdf(label_data, tracking_number) filename = "early_ship_label_#{tracking_number}_#{Time.current.to_i}.pdf" upload = Upload.uploadify_from_data( file_name: filename, data: label_data, category: 'early_ship_label', resource: self ) if upload&.persisted? uploads << upload Rails.logger.info("[EarlyLabel] Stored early label PDF as upload #{upload.id}") end upload rescue StandardError => e Rails.logger.error("[EarlyLabel] Failed to store early label PDF: #{e.}") nil end |
#store_id ⇒ Object
Alias for Store#id
2227 |
# File 'app/models/order.rb', line 2227 delegate :id, to: :store, prefix: true |
#store_mock_early_label_pdf(tracking_number, _carrier) ⇒ Object
Store a mock label PDF for development/testing
Uses a pre-generated mock PDF since the sandbox doesn't return real label PDFs
4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 |
# File 'app/models/order.rb', line 4054 def store_mock_early_label_pdf(tracking_number, _carrier) mock_pdf_path = Rails.root.join('test/sww_label_mock_pdf.pdf') unless File.exist?(mock_pdf_path) Rails.logger.error("[EarlyLabel] Mock PDF not found at #{mock_pdf_path}") return nil end mock_pdf_content = File.binread(mock_pdf_path) filename = "early_ship_label_#{tracking_number}_#{Time.current.to_i}.pdf" upload = Upload.uploadify_from_data( file_name: filename, data: mock_pdf_content, category: 'early_ship_label', resource: self ) if upload&.persisted? uploads << upload Rails.logger.info("[EarlyLabel] Stored mock early label PDF as upload #{upload.id}") end upload rescue StandardError => e Rails.logger.error("[EarlyLabel] Failed to store mock early label PDF: #{e.}") nil end |
#store_walmart_label_pdf_atomic(shipper, _label_result, tracking_number, carrier, marketplace) ⇒ Object
Walmart: download label via API with retries, then store and verify persistence
3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 |
# File 'app/models/order.rb', line 3974 def store_walmart_label_pdf_atomic(shipper, _label_result, tracking_number, carrier, marketplace) max_attempts = 3 delays = [1, 2, 3] max_attempts.times do |attempt| delay = delays[attempt] || 1 sleep(delay) Rails.logger.info("[EarlyLabel] Attempting PDF download (attempt #{attempt + 1}/#{max_attempts}) for tracking #{tracking_number}") download_result = shipper.download_label(carrier, tracking_number) merge_shipper_api_log(shipper) if download_result[:success] && download_result[:label_data].present? upload = store_early_label_pdf(download_result[:label_data], tracking_number) if upload&.persisted? Rails.logger.info("[EarlyLabel] PDF successfully stored (attempt #{attempt + 1}) as upload #{upload.id}") return upload else Rails.logger.warn("[EarlyLabel] PDF download succeeded but storage failed (attempt #{attempt + 1})") end else Rails.logger.warn("[EarlyLabel] PDF download failed (attempt #{attempt + 1}): #{download_result[:error]}") end end Rails.logger.error("[EarlyLabel] All PDF download attempts failed for order #{reference_number}, tracking #{tracking_number}") send_early_label_void_alert( reason: 'PDF download failed after all retries', details: "Label was created successfully but PDF could not be downloaded after #{max_attempts} attempts. " \ "The label exists in #{marketplace}'s system but is not stored locally." ) nil end |
#suggested_shipments ⇒ Object
4453 4454 4455 |
# File 'app/models/order.rb', line 4453 def suggested_shipments shipments.where(state: 'suggested') if respond_to? :shipments end |
#support_case_ref ⇒ Object
1287 1288 1289 |
# File 'app/models/order.rb', line 1287 def support_case_ref support_cases.collect(&:case_number).join(', ') end |
#support_case_ref=(support_case_ref) ⇒ Object
1291 1292 1293 1294 |
# File 'app/models/order.rb', line 1291 def support_case_ref=(support_case_ref) refs = support_case_ref.delete(' ').split(',') self.support_case_ids = SupportCase.where(case_number: refs).pluck(:id) end |
#support_cases ⇒ ActiveRecord::Relation<SupportCase>
294 |
# File 'app/models/order.rb', line 294 has_and_belongs_to_many :support_cases |
#suppress_edi_duplicate_warning? ⇒ Boolean
Suppresses duplicate order warnings for off-book delivery arrangements.
Returns true if warnings should be suppressed (acknowledged date is in the future and order not shipped).
1610 1611 1612 1613 1614 1615 |
# File 'app/models/order.rb', line 1610 def suppress_edi_duplicate_warning? return false if edi_delayed_delivery_acknowledged_at.blank? return false if shipped? # Auto-clear when shipped edi_delayed_delivery_acknowledged_at.to_date >= Date.current end |
#sync_opportunity ⇒ Object
2857 2858 2859 |
# File 'app/models/order.rb', line 2857 def sync_opportunity opportunity&.sync_state end |
#technical_support_rep ⇒ TechnicalSupportRep
271 |
# File 'app/models/order.rb', line 271 has_one :technical_support_rep, through: :opportunity |
#terms_available? ⇒ Boolean (protected)
4609 4610 4611 |
# File 'app/models/order.rb', line 4609 def terms_available? billing_entity.terms_credit_limit >= total end |
#to_label ⇒ Object
2801 2802 2803 |
# File 'app/models/order.rb', line 2801 def to_label "[#{reference_number}] #{customer.full_name} (#{shipped_date.to_fs(:crm_default)})" end |
#to_liquid ⇒ Object
2445 2446 2447 |
# File 'app/models/order.rb', line 2445 def to_liquid Liquid::OrderDrop.new self end |
#to_s ⇒ Object
2818 2819 2820 |
# File 'app/models/order.rb', line 2818 def to_s cart_identifier end |
#to_store ⇒ Store
265 |
# File 'app/models/order.rb', line 265 belongs_to :to_store, class_name: 'Store', optional: true |
#total_cod ⇒ Object
2266 2267 2268 2269 2270 2271 2272 2273 |
# File 'app/models/order.rb', line 2266 def total_cod # return 0 if the order is not funded by cod return 0.0 unless funded_by_cod? # else we need to deduct any pre_payments from the order total to work out what the cod amount should be # in case the balance was partially paid by another payment method total - end |
#total_money ⇒ Object
1469 1470 1471 |
# File 'app/models/order.rb', line 1469 def total_money '%.2f' % total end |
#total_payments_authorized ⇒ Object
1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 |
# File 'app/models/order.rb', line 1912 def amount = BigDecimal('0.0') deliveries.each do |dq| dq_total = dq.total || BigDecimal('0.0') dq_auth = dq. || BigDecimal('0.0') amount += if dq_auth >= dq_total dq_total else dq_auth end end amount < 0 ? BigDecimal('0.0') : amount end |
#track_profit? ⇒ Boolean
4445 4446 4447 |
# File 'app/models/order.rb', line 4445 def track_profit? profitable_line_items.present? && is_regular_order? end |
#tracking_email_address ⇒ Object
1867 1868 1869 1870 1871 1872 |
# File 'app/models/order.rb', line 1867 def tracking_email_address domain = Rails.application.config.x.email_domain prefix = 'ord' encrypted_id = Encryption.encrypt_string(id.to_s) "#{prefix}+id#{encrypted_id}@#{domain}" end |
#uncommit_undelivered_line_items ⇒ Object
1982 1983 1984 1985 1986 |
# File 'app/models/order.rb', line 1982 def uncommit_undelivered_line_items orphaned = line_items.where(delivery_id: nil) .joins(:inventory_commits).distinct Item::InventoryCommitter.crm_uncommit(orphaned) if orphaned.any? end |
#unfulfilled_dropship_items ⇒ Object
1988 1989 1990 |
# File 'app/models/order.rb', line 1988 def unfulfilled_dropship_items line_items.dropship.any? { |li| li.purchase_order_item.nil? || !li.purchase_order_item.fully_receipted? } end |
#update_customer_status ⇒ Object (protected)
4583 4584 4585 |
# File 'app/models/order.rb', line 4583 def update_customer_status customer&.set_status end |
#update_linked_po_if_st ⇒ Object
4438 4439 4440 4441 4442 4443 |
# File 'app/models/order.rb', line 4438 def update_linked_po_if_st return unless is_store_transfer? && deliveries.first po = PurchaseOrder.new_or_update_st_po_from_delivery(deliveries.first) po.present? && po.valid? end |
#update_sales_support_rep(new_sales_support_rep_id, new_commission_date = nil) ⇒ Object
1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 |
# File 'app/models/order.rb', line 1536 def update_sales_support_rep(new_sales_support_rep_id, new_commission_date = nil) # Allow updating sales_support_rep_id and commission date even for invoiced orders if invoiced? # For invoiced orders, update only the sales_support_rep_id and commission date without other validations self.sales_support_rep_id = new_sales_support_rep_id self.sales_support_commission_date = new_commission_date save(validate: false) else # For non-invoiced orders, use normal update process update( sales_support_rep_id: new_sales_support_rep_id, sales_support_commission_date: new_commission_date ) end end |
#uploads ⇒ ActiveRecord::Relation<Upload>
276 |
# File 'app/models/order.rb', line 276 has_many :uploads, -> { order(created_at: :desc) }, as: :resource, dependent: :destroy |
#versions_for_audit_trail(_params = {}) ⇒ Object
2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 |
# File 'app/models/order.rb', line 2861 def versions_for_audit_trail(_params = {}) query_sql = %q{ (item_type = 'Order' and item_id = :id) OR ( item_type = 'LineItem' AND reference_data @> '{"resource_type": "Order"}' AND reference_data @> '{"resource_id": :id}' ) OR ( item_type = 'Delivery' AND reference_data @> '{"order_id": :id}' ) OR ( item_type = 'Discount' AND reference_data @> '{"itemizable_id": :id}' AND reference_data @> '{"itemizable_type": "Order"}' ) } RecordVersion.where(query_sql, id:) end |
#void_credit_rma_item ⇒ Object
2886 2887 2888 |
# File 'app/models/order.rb', line 2886 def void_credit_rma_item line_items.map(&:credit_rma_item).compact.uniq.each(&:void!) end |
#void_early_label!(reason: 'Manual void requested', reset_flag: true) ⇒ Hash
Void the early label with a custom reason (public method for external callers)
Also resets purchase_label_early flag so the next ship-label goes through normal flow
3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 |
# File 'app/models/order.rb', line 3446 def void_early_label!(reason: 'Manual void requested', reset_flag: true) return { success: true, message: 'No early label to void' } unless has_early_purchased_label? Rails.logger.info("[EarlyLabel] Voiding early label for order #{reference_number}: tracking=#{early_label_tracking_number}, reason=#{reason}") begin client, marketplace_type = build_early_label_void_client void_api_result = if marketplace_type == :amazon client.cancel_shipment(early_label_sww_label_id) else client.discard_label(early_label_carrier, early_label_tracking_number) end if void_api_result.success Rails.logger.info("[EarlyLabel] Successfully voided early label for order #{reference_number}") void_additional_early_label_packages(client) if marketplace_type == :amazon update_attrs = { early_label_voided_at: Time.current, early_label_void_reason: reason } update_attrs[:purchase_label_early] = false if reset_flag update!(update_attrs) void_early_label_upload Rails.logger.info('[EarlyLabel] Reset purchase_label_early flag') if reset_flag { success: true } else Rails.logger.error("[EarlyLabel] Failed to void early label for order #{reference_number}: #{void_api_result.error}") update!(early_label_void_reason: "#{reason} (API failed: #{void_api_result.error})") { success: false, error: void_api_result.error } end rescue StandardError => e Rails.logger.error("[EarlyLabel] Error voiding early label for order #{reference_number}: #{e.}") update!(early_label_void_reason: "#{reason} (Exception: #{e.})") { success: false, error: e. } end end |
#void_early_label_if_exists ⇒ Hash?
Void early-purchased label when order is pulled back from awaiting_deliveries
Called from before_transition to cancelled/fraudulent/in_cr_hold
SAFEGUARD B: Blocks rapid void if label was just purchased (within threshold)
SAFEGUARD C: Sends alert email if void fails
3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 |
# File 'app/models/order.rb', line 3191 def void_early_label_if_exists return unless has_early_purchased_label? # SAFEGUARD B: Check for rapid state transition if early_label_purchased_recently? minutes_since = ((Time.current - early_label_purchased_at) / 60).round(1) wait_minutes = (EARLY_LABEL_RAPID_VOID_THRESHOLD_MINUTES - minutes_since).ceil Rails.logger.warn("[EarlyLabel] Rapid void blocked for order #{reference_number}: label purchased #{minutes_since} min ago (threshold: #{EARLY_LABEL_RAPID_VOID_THRESHOLD_MINUTES} min)") errors.add(:base, "Early label was purchased #{minutes_since} minutes ago. Please wait #{wait_minutes} more minute(s) before holding/canceling, or void the label manually first.") throw :halt end Rails.logger.info("[EarlyLabel] Auto-voiding early label for order #{reference_number}: tracking=#{early_label_tracking_number}") # Initialize API log for void operation @early_label_api_log = [] log_early_label_event('void_start', "Starting void for tracking #{early_label_tracking_number}") begin client, marketplace_type = build_early_label_void_client void_api_result = if marketplace_type == :amazon client.cancel_shipment(early_label_sww_label_id) else client.discard_label(early_label_carrier, early_label_tracking_number) end log_early_label_event('api_response', 'void/discard response', { success: void_api_result.success, error: void_api_result.error }) if void_api_result.success Rails.logger.info("[EarlyLabel] Successfully voided early label for order #{reference_number}") log_early_label_event('void_success', "Label voided successfully") void_additional_early_label_packages(client) if marketplace_type == :amazon save_early_label_api_log update!( early_label_voided_at: Time.current, early_label_void_reason: 'Order transitioned out of awaiting_deliveries' ) void_early_label_upload { success: true } else Rails.logger.error("[EarlyLabel] Failed to void early label for order #{reference_number}: #{void_api_result.error}") log_early_label_event('void_failed', "Void API failed: #{void_api_result.error}") save_early_label_api_log # SAFEGUARD C: Send alert for void failure marketplace = edi_orchestrator_partner&.start_with?('amazon_seller') ? 'Amazon' : 'Walmart' send_early_label_void_alert( reason: "#{marketplace} API void failed", details: void_api_result.error ) update!(early_label_void_reason: "Void attempted but API failed: #{void_api_result.error}") { success: false, error: void_api_result.error } end rescue StandardError => e Rails.logger.error("[EarlyLabel] Error voiding early label for order #{reference_number}: #{e.}") log_early_label_event('void_exception', "Exception: #{e.}") save_early_label_api_log # SAFEGUARD C: Send alert for void exception (unless it's our own rapid-void exception) unless e..include?('Cannot auto-void early label') send_early_label_void_alert( reason: 'Void exception', details: e. ) end update!(early_label_void_reason: "Void failed with exception: #{e.}") { success: false, error: e. } end end |
#void_early_label_upload ⇒ Object
Re-categorize the early label PDF as voided so it no longer appears
as an active attachment but is kept for audit trail (mirrors the
ship_label_pdf -> voided_ship_label_pdf pattern in Shipment)
3592 3593 3594 3595 3596 3597 3598 |
# File 'app/models/order.rb', line 3592 def void_early_label_upload uploads.in_category('early_ship_label').update_all(category: 'voided_early_ship_label') Rails.logger.info("[EarlyLabel] Re-categorized early label PDF to voided_early_ship_label on order #{reference_number}") rescue StandardError => e Rails.logger.warn("[EarlyLabel] Failed to re-categorize early label PDF: #{e.}") # Non-fatal — metadata void is what matters end |
#void_payments(report_fraud = false) ⇒ Object
4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 |
# File 'app/models/order.rb', line 4569 def void_payments(report_fraud = false) payments.each do |pp| if pp. res = pp.gateway_class.new(pp).void(report_fraud) raise StandardError, "Unable to void authorized payment #{pp.id} for order #{reference_number}" unless res.success elsif pp.captured? # TODO: Handle captured payments. raise StandardError, "Unable to void captured payments #{pp.id} for order #{reference_number}" end end end |
#vouchers ⇒ ActiveRecord::Relation<Voucher>
282 |
# File 'app/models/order.rb', line 282 has_many :vouchers |
#walmart_sww_eligible? ⇒ Boolean
Check if order is eligible for Walmart Ship with Walmart early label purchase
3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 |
# File 'app/models/order.rb', line 3492 def walmart_sww_eligible? return false unless is_edi_order? return false unless edi_orchestrator_partner&.start_with?('walmart_seller') begin orchestrator = Edi::Walmart::Orchestrator.new(edi_orchestrator_partner.to_sym) orchestrator&.ship_with_walmart_enabled? rescue StandardError => e Rails.logger.warn("[EarlyLabel] Error checking SWW eligibility: #{e.}") false end end |