Class: Quote
- Inherits:
-
ApplicationRecord
show all
- Includes:
- Memery, Models::Auditable, Models::CouponDateOverridable, Models::Itemizable, Models::LegacyRateRequest, Models::Lineage, Models::MultiRoom, Models::Notable, Models::Pickable, Models::Profitable, Models::ShipQuotable, Models::TaxableResource, PgSearch::Model
- Defined in:
- app/models/quote.rb
Overview
== Schema Information
Table name: quotes
Database name: primary
id :integer not null, primary key
ancestors_count :integer
bill_shipping_to_customer :boolean
children_count :integer
complete_datetime :datetime
currency :string(255)
descendants_count :integer
disable_auto_coupon :boolean default(FALSE), not null
discount :decimal(10, 2)
expiration_date :date
expiration_notice_sent :boolean default(FALSE), not null
first_view_date :datetime
hidden :boolean default(FALSE), not null
hold_for_transmission :boolean default(FALSE), not null
jde_b_ab :integer
jde_s_ab :integer
line_offset :decimal(10, 2)
line_total :decimal(10, 2)
line_total_discounted :decimal(10, 2)
ltl_freight :boolean
ltl_freight_guaranteed :boolean
max_discount_override :decimal(4, 2)
min_profit_markup :integer default(0)
override_coupon_date :date
override_coupon_date_without_limits :boolean default(FALSE), not null
override_line_lock :boolean default(FALSE), not null
position :integer default(100)
pricing_program_description :string(255)
pricing_program_discount :integer
priority :string(255)
quote_type :string(2)
recalculate_discounts :boolean default(FALSE), not null
recalculate_shipping :boolean default(TRUE), not null
reference_number :string(255) not null
requires_upgrade :boolean
retrieving_shipping_costs :boolean
saturday_delivery :boolean default(FALSE), not null
shipping_cost :decimal(10, 2)
shipping_coupon :decimal(10, 2)
shipping_issue_alerted :boolean
shipping_method :string(255)
ships_economy :boolean default(FALSE), not null
signature_confirmation :boolean default(FALSE), not null
single_origin :boolean default(FALSE), not null
state :string(255)
suffix :string(255)
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)
uploads_count :integer default(0)
created_at :datetime
updated_at :datetime
billing_address_id :integer
buying_group_id :integer
creator_id :integer
legacy_proto_iq_room_id :integer
opportunity_id :integer
parent_id :integer
resource_tax_rate_id :integer
rma_id :integer
shipping_address_id :integer
updater_id :integer
visit_id :bigint
Indexes
idx_quotes_reference_number_trgm_gist (reference_number) USING gist
idx_quotes_rma_id (rma_id)
index_quotes_on_legacy_proto_iq_room_id (legacy_proto_iq_room_id)
index_quotes_on_parent_id (parent_id)
index_quotes_on_reference_number (reference_number) UNIQUE
index_quotes_on_suffix (suffix) USING gin
index_quotes_on_visit_id (visit_id) WHERE (visit_id IS NOT NULL) USING hash
quotes_aasm_state_index (state)
quotes_opportunity_id_index (opportunity_id)
quotes_shipping_address_id_idx (shipping_address_id)
Foreign Keys
fk_rails_... (shipping_address_id => addresses.id)
fk_rails_... (visit_id => visits.id) ON DELETE => nullify
Defined Under Namespace
Classes: AssignLargeOppActivity, CombinedPdfGenerator, ConvertToOrder, Copier, LetterPdfGenerator, Mover, QuoteCompletedHandler, Reviser
Constant Summary
collapse
- SALES_QUOTE =
'SQ'
- MARKETING_QUOTE =
'MQ'
- TECH_QUOTE =
'TQ'
- INSTANT_QUOTE =
'IQ'
- QUOTE_TYPES =
%w[SQ MQ TQ IQ].freeze
- REFERENCE_NUMBER_PATTERN =
Regexp.new("^(#{QUOTE_TYPES.join('|')})(\\d+)(-R\\d+)?$", Regexp::IGNORECASE)
- CORK_MIN_QTY =
1
- MIN_INSTALL_SQ_FT =
88
- CUSTOMER_CANCELABLE_STATES =
%i[pending].freeze
- CANCELABLE_STATES =
CUSTOMER_CANCELABLE_STATES + %i[pending_project_details awaiting_completed_installation_plans awaiting_transmission]
- CLOSED_STATES =
%i[complete cancelled].freeze
- CAN_REQUEST_PRE_PACK_STATES =
%i[pending pre_pack awaiting_transmission profit_review].freeze
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
#min_profit_markup
#force_total_reset, #total_reset
Delegated Instance Attributes
collapse
#creator, #updater
#shipping_address
#resource_tax_rate
#account_specialist
#deliveries, #shipments
#line_discounts, #line_items
#coupons, #discounts
Has and belongs to many
collapse
#room_configurations
Class Method Summary
collapse
Instance Method Summary
collapse
#override_coupon_date_limit
#last_shipping_rate_request_result, #last_shipping_rate_request_result=
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
#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?
#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
#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, #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
#carrier, #chosen_shipping_method, #days_commitment, #determine_origin_address, #everything_in_stock?, #friendly_shipping_method, #is_drop_ship?, #is_override?, #is_warehouse_pickup?, #line_items_grouped_by_deliveries_quoting, #line_items_match_deliveries_if_any, #need_to_pre_pack_reasons, #need_to_recalculate_shipping, #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, #ship_weight, #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
#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
#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
#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
#add_line_item, #additional_items, #assign_sequence, #billing_entity, #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, #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
#quick_note
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
#publish_event
Instance Attribute Details
#do_not_detect_shipping ⇒ Object
Returns the value of attribute do_not_detect_shipping.
145
146
147
|
# File 'app/models/quote.rb', line 145
def do_not_detect_shipping
@do_not_detect_shipping
end
|
#do_not_set_totals ⇒ Object
Returns the value of attribute do_not_set_totals.
145
146
147
|
# File 'app/models/quote.rb', line 145
def do_not_set_totals
@do_not_set_totals
end
|
#max_discount_override ⇒ Object
187
|
# File 'app/models/quote.rb', line 187
validates :max_discount_override, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100, allow_nil: true }
|
#opportunity_id ⇒ Object
184
|
# File 'app/models/quote.rb', line 184
validates :opportunity_id, presence: { if: proc { |q| q.validate_opportunity_and_prepared_for } }
|
#suffix ⇒ Object
185
|
# File 'app/models/quote.rb', line 185
validates :suffix, length: { maximum: 150 }
|
#validate_opportunity_and_prepared_for ⇒ Object
Returns the value of attribute validate_opportunity_and_prepared_for.
145
146
147
|
# File 'app/models/quote.rb', line 145
def validate_opportunity_and_prepared_for
@validate_opportunity_and_prepared_for
end
|
Class Method Details
.active ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are active. Active Record Scope
199
|
# File 'app/models/quote.rb', line 199
scope :active, -> { where.not(state: 'cancelled') }
|
.auto_quote_from_room(room, options = {}) ⇒ Object
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
|
# File 'app/models/quote.rb', line 842
def self.auto_quote_from_room(room, options = {})
force_single_origin = options[:force_single_origin] || true
do_not_set_shipping_address = options[:do_not_set_shipping_address] || false
target_state_event = options[:state_event] || 'complete'
q = Quote.new(opportunity_id: room.opportunity_id)
q.shipping_address_id = room.customer.shipping_address_id if !room.installation_postal_code && (do_not_set_shipping_address == false) && room.customer && room.customer.shipping_address.present?
q.single_origin = true if force_single_origin
q.room_configuration_ids = [room.id]
q.do_not_detect_shipping = true
q.save
if q.persisted?
q.recalculate_discounts = true
q.force_total_reset = true
q.do_not_detect_shipping = true q.save
end
if q.persisted? && target_state_event.present? && q.respond_to?("#{target_state_event}!")
q.do_not_set_totals = true q.send("#{target_state_event}!")
end
q
end
|
.by_reference_number ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are by reference number. Active Record Scope
195
|
# File 'app/models/quote.rb', line 195
scope :by_reference_number, -> { order(:reference_number).reverse_order }
|
.completed_quotes ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are completed quotes. Active Record Scope
191
|
# File 'app/models/quote.rb', line 191
scope :completed_quotes, -> { where(state: 'complete') }
|
.contains_coupon_ids ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are contains coupon ids. Active Record Scope
198
|
# File 'app/models/quote.rb', line 198
scope :contains_coupon_ids, ->(coupon_ids) { where("EXISTS (select discounts.id from discounts where discounts.coupon_id IN (?) and discounts.itemizable_type = 'Quote' and discounts.itemizable_id = quotes.id)", coupon_ids) }
|
.contains_item_ids ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are contains item ids. Active Record Scope
197
|
# File 'app/models/quote.rb', line 197
scope :contains_item_ids, ->(item_ids) { where("EXISTS (select 1 from line_items li where li.resource_id = quotes.id and li.resource_type = 'Quote' and li.item_id IN (?))", item_ids) }
|
.count_by_incomplete_state_by_rep(rep_id = nil, sales_rep_type_id_label = 'primary_sales_rep_id') ⇒ Object
781
782
783
784
785
786
787
|
# File 'app/models/quote.rb', line 781
def self.count_by_incomplete_state_by_rep(rep_id = nil, sales_rep_type_id_label = 'primary_sales_rep_id')
if rep_id.nil?
count(:state, conditions: ["state not in ('complete','cancelled')"], group: :state)
else
count(:state, conditions: ["state not in ('complete','cancelled') and " + sales_rep_type_id_label.to_s + ' = ?', rep_id], group: :state)
end
end
|
452
453
454
|
# File 'app/models/quote.rb', line 452
def self.formatted_resources_query(quotes)
quotes.select('quotes.reference_number,quotes.id,quotes.created_at,quotes.state,opportunities.name as opportunity_name').joins(:opportunity).order('reference_number desc')
end
|
.ignore_fixture_attributes ⇒ Object
444
445
446
|
# File 'app/models/quote.rb', line 444
def self.ignore_fixture_attributes
%w[last_rate_request_xml last_rate_response_xml]
end
|
.in_quoting ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are in quoting. Active Record Scope
201
|
# File 'app/models/quote.rb', line 201
scope :in_quoting, -> { where(state: %w[awaiting_completed_installation_plans pre_pack awaiting_transmission]) }
|
.last_quote_update_cache_key ⇒ Object
448
449
450
|
# File 'app/models/quote.rb', line 448
def self.last_quote_update_cache_key
Quote.select('id,updated_at').order(:updated_at).reverse_order.first.cache_key
end
|
.last_revisions ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are last revisions. Active Record Scope
196
|
# File 'app/models/quote.rb', line 196
scope :last_revisions, -> { where("NOT EXISTS (select id from quotes q2 where q2.parent_id = quotes.id and quotes.state <> 'cancelled')") }
|
.like_lookup ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are like lookup. Active Record Scope
203
|
# File 'app/models/quote.rb', line 203
scope :like_lookup, ->(q) { where('quotes.reference_number ILIKE :term', { term: "%#{q}%" }) }
|
.lookup ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are lookup. Active Record Scope
202
|
# File 'app/models/quote.rb', line 202
scope :lookup, ->(q) { where('quotes.reference_number ILIKE :term', { term: q }) }
|
.most_recent_first ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are most recent first. Active Record Scope
194
|
# File 'app/models/quote.rb', line 194
scope :most_recent_first, -> { order(:created_at).reverse_order }
|
.not_converted ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are not converted. Active Record Scope
200
|
# File 'app/models/quote.rb', line 200
scope :not_converted, -> { where("not exists(select 1 from orders where quote_id = quotes.id and orders.state = 'invoiced')") }
|
.open_quotes ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are open quotes. Active Record Scope
190
|
# File 'app/models/quote.rb', line 190
scope :open_quotes, -> { where.not(state: %w[complete cancelled]) }
|
.pending ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are pending. Active Record Scope
192
|
# File 'app/models/quote.rb', line 192
scope :pending, -> { where(state: 'pending') }
|
.sales_quotes ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are sales quotes. Active Record Scope
206
|
# File 'app/models/quote.rb', line 206
scope :sales_quotes, -> { where(quote_type: SALES_QUOTE) }
|
.states_for_select ⇒ Object
440
441
442
|
# File 'app/models/quote.rb', line 440
def self.states_for_select
state_machines[:state].states.map { |s| [s.human_name, s.value] }
end
|
A relation of Quotes that are with contact point category. Active Record Scope
204
|
# File 'app/models/quote.rb', line 204
scope :with_contact_point_category, ->(category) { where('exists(select 1 from contact_points_quotes cq inner join contact_points cp on cp.id = cq.contact_point_id and cq.quote_id = quotes.id where cp.category = ?)', category) }
|
.with_email ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are with email. Active Record Scope
205
|
# File 'app/models/quote.rb', line 205
scope :with_email, -> { with_contact_point_category(ContactPoint::EMAIL) }
|
.with_line_items ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are with line items. Active Record Scope
193
|
# File 'app/models/quote.rb', line 193
scope :with_line_items, -> { includes(line_items: { catalog_item: { store_item: :item } }) }
|
.without_quofus ⇒ ActiveRecord::Relation<Quote>
A relation of Quotes that are without quofus. Active Record Scope
207
208
209
210
211
212
213
214
215
216
|
# File 'app/models/quote.rb', line 207
scope :without_quofus, -> {
where(%{
not exists(
select 1 from activities a
where (
(a.resource_id = quotes.opportunity_id and a.resource_type = 'Opportunity')
or (a.resource_id = quotes.id and a.resource_type = 'Quote')
) and a.activity_type_id IN (?)
) }, ActivityTypeConstants::QUOFUS_IDS)
}
|
Instance Method Details
#active_orders ⇒ ActiveRecord::Relation<Order>
162
|
# File 'app/models/quote.rb', line 162
has_many :active_orders, -> { where(Order[:state].not_in(%w[cancelled fraudulent])) }, class_name: 'Order'
|
#activities ⇒ ActiveRecord::Relation<Activity>
164
|
# File 'app/models/quote.rb', line 164
has_many :activities, as: :resource, dependent: :nullify
|
#addendums ⇒ Object
582
583
584
585
586
587
588
589
590
591
592
|
# File 'app/models/quote.rb', line 582
def addendums
list = []
current_month = Date.current.strftime('%B-%Y')
publications = Item.publications.with_all_tags('for-quote', current_month.downcase)
.or(Item.publications.with_all_tags('for-quote', 'permanent'))
publications.each do |p|
list << p.upload
end
list.uniq.compact
end
|
#address_book ⇒ Object
936
937
938
|
# File 'app/models/quote.rb', line 936
def address_book
[customer.store.warehouse_address] + customer.addresses
end
|
#adjust_status_based_on_room_status(rc) ⇒ Object
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
|
# File 'app/models/quote.rb', line 702
def adjust_status_based_on_room_status(rc)
Rails.logger.info "Quote Ref and ID #{reference_number}, #{id}, quote.adjust_status_based_on_room_status: self.state: #{state}, rc: #{rc.reference_number}, #{rc.id}, rc state: #{rc.state}, DateTime: #{Time.current}"
self.do_not_set_totals = true
if rc.complete? || rc.cancelled?
Rails.logger.info "Quote Ref and ID #{reference_number}, #{id}, quote.adjust_status_based_on_room_status, BEFORE installation_plans_complete: self.state: #{state}, rc: #{rc.reference_number}, rc state: #{rc.state}"
res = reload.installation_plans_complete unless complete?
Rails.logger.info "Quote Ref and ID #{reference_number}, #{id}, quote.adjust_status_based_on_room_status, AFTER installation_plans_complete: self.state: #{state}, rc: #{rc.reference_number}, rc state: #{rc.state}, result of installation_plans_complete: #{res}"
elsif rc.draft?
pending_project_details if can_pending_project_details? && awaiting_completed_installation_plans?
elsif rc.in_design?
Rails.logger.info "Quote Ref and ID #{reference_number}, #{id}, quote.adjust_status_based_on_room_status, BEFORE ready_for_design: self.state: #{state}, rc: #{rc.reference_number}, rc state: #{rc.state}"
res = ready_for_design
Rails.logger.info "Quote Ref and ID #{reference_number}, #{id}, quote.adjust_status_based_on_room_status, AFTER ready_for_design: self.state: #{state}, rc: #{rc.reference_number}, rc state: #{rc.state}, result of ready_for_design: #{res}"
end
end
|
#all_activities ⇒ Object
show all activities linked to this quote as well as its parent opportunity
932
933
934
|
# File 'app/models/quote.rb', line 932
def all_activities
opportunity.linked_activities
end
|
#all_orders_sold? ⇒ Boolean
523
524
525
|
# File 'app/models/quote.rb', line 523
def all_orders_sold?
orders.any? && orders.all?(&:invoiced?)
end
|
#all_participants ⇒ Object
Alias for Opportunity#all_participants
537
|
# File 'app/models/quote.rb', line 537
delegate :all_participants, to: :opportunity
|
#all_suggested_items ⇒ Object
547
548
549
550
551
552
553
554
555
556
557
558
|
# File 'app/models/quote.rb', line 547
def all_suggested_items
goods = suggested_items
services = suggested_services
unique_goods = []
goods.each do |_rc, lines|
lines.each do |li|
unique_goods << li unless unique_goods.any? { |si| si.item == li.item }
end
end
unique_goods_skus = unique_goods.compact.uniq.collect(&:sku)
{ goods:, services:, unique_goods:, unique_goods_skus: }
end
|
#all_support_cases ⇒ Object
422
423
424
425
426
|
# File 'app/models/quote.rb', line 422
def all_support_cases
support_case_ids
linked_support_cases.pluck(:id)
SupportCase.where(id: support_case_ids).order(Arel.sql('support_cases.case_number desc'))
end
|
#applies_for_smartinstall ⇒ Object
948
949
950
951
952
953
|
# File 'app/models/quote.rb', line 948
def applies_for_smartinstall
return false return true if installation_is_within_range? && customer.is_homeowner? && has_selected_heated_items?
false
end
|
#applies_for_smartsupport ⇒ Object
955
956
957
958
959
|
# File 'app/models/quote.rb', line 955
def applies_for_smartsupport
return true if customer.is_dealer_or_trade_pro_for_locator? && has_selected_heated_items?
false
end
|
#apply_tier2_pricing? ⇒ Boolean
Whether or not to apply the tier2 pricing (customer discount) by default
414
415
416
|
# File 'app/models/quote.rb', line 414
def apply_tier2_pricing?
is_sales_quote?
end
|
606
607
608
609
610
611
|
# File 'app/models/quote.rb', line 606
def available_contact_points
cps = opportunity.primary_party.contact_points.order(:position).to_a
cps += opportunity.customer.contact_points.order(:position).to_a
cps += opportunity.opportunity_participants.map { |op| op.party.contact_points.order(:position).to_a }.flatten
cps.uniq
end
|
613
614
615
616
617
618
|
# File 'app/models/quote.rb', line 613
def available_contact_points_grouped_for_select
available_contact_points.each_with_object({}) do |cp, hsh|
hsh[cp.party.full_name] ||= []
hsh[cp.party.full_name] << ["#{cp.detail} (#{cp.category})", cp.id]
end
end
|
594
595
596
597
598
599
600
601
602
603
604
|
# File 'app/models/quote.rb', line 594
def best_contact_point
contact_points.transmittable.first || (begin
contact.contact_points.transmittable.first
rescue StandardError
nil
end) || (begin
customer.contact_points.transmittable.first
rescue StandardError
nil
end)
end
|
#billing_address ⇒ Object
Alias for Customer#billing_address
152
|
# File 'app/models/quote.rb', line 152
delegate :billing_address, :company, to: :customer
|
#build_activity ⇒ Object
418
419
420
|
# File 'app/models/quote.rb', line 418
def build_activity
activities.build resource: self, party: primary_party
end
|
157
|
# File 'app/models/quote.rb', line 157
belongs_to :buying_group, optional: true
|
#calculate_expiration_date ⇒ Object
665
666
667
668
669
670
671
672
|
# File 'app/models/quote.rb', line 665
def calculate_expiration_date
expirations = [] + (begin
coupons.map(&:expiration_date).compact
rescue StandardError
[]
end) + [created_at + 60.days]
expirations.min.to_date
end
|
#can_be_moved? ⇒ Boolean
428
429
430
|
# File 'app/models/quote.rb', line 428
def can_be_moved?
orders.active.non_carts.blank?
end
|
#can_be_transmitted? ⇒ Boolean
514
515
516
|
# File 'app/models/quote.rb', line 514
def can_be_transmitted?
awaiting_transmission? || pending_project_details? || complete?
end
|
#can_convert? ⇒ Object
Alias for Convert_to_order_service#can_convert?
744
|
# File 'app/models/quote.rb', line 744
delegate :can_convert?, to: :convert_to_order_service
|
#cancelable? ⇒ Boolean
773
774
775
|
# File 'app/models/quote.rb', line 773
def cancelable?
orders.not_cancelled.empty?
end
|
#catalog ⇒ Object
Alias for Customer#catalog
151
|
# File 'app/models/quote.rb', line 151
delegate :catalog, to: :customer
|
#check_pre_pack ⇒ Object
1033
1034
1035
|
# File 'app/models/quote.rb', line 1033
def check_pre_pack
request_estimated_packaging if awaiting_transmission? && can_request_estimated_packaging? && !pre_pack? && !deliveries.all? { |d| d.pre_pack? } && stop_for_pre_pack?
end
|
#communications ⇒ ActiveRecord::Relation<Communication>
165
|
# File 'app/models/quote.rb', line 165
has_many :communications, as: :resource, dependent: :nullify
|
#company ⇒ Object
Alias for Customer#company
152
|
# File 'app/models/quote.rb', line 152
delegate :billing_address, :company, to: :customer
|
Alias for Opportunity#contact
149
|
# File 'app/models/quote.rb', line 149
delegate :customer, :contact, :primary_party, to: :opportunity
|
170
|
# File 'app/models/quote.rb', line 170
has_and_belongs_to_many :contact_points, inverse_of: :quotes
|
#convert_to_order_service ⇒ Object
740
741
742
|
# File 'app/models/quote.rb', line 740
def convert_to_order_service
@convert_to_order_service ||= Quote::ConvertToOrder.new(self)
end
|
#country ⇒ Object
1041
1042
1043
1044
1045
|
# File 'app/models/quote.rb', line 1041
def country
customer.catalog.store.country
rescue StandardError
nil
end
|
#crm_link ⇒ Object
736
737
738
|
# File 'app/models/quote.rb', line 736
def crm_link
UrlHelper.instance.quote_path(self)
end
|
#currency_symbol ⇒ Object
661
662
663
|
# File 'app/models/quote.rb', line 661
def currency_symbol
Money::Currency.new(currency).symbol
end
|
#customer ⇒ Object
Alias for Opportunity#customer
149
|
# File 'app/models/quote.rb', line 149
delegate :customer, :contact, :primary_party, to: :opportunity
|
#deep_dup ⇒ Object
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# File 'app/models/quote.rb', line 114
def deep_dup
deep_clone(include: :contact_points) do |original, copy|
next unless copy.is_a?(Quote)
copy.state = 'pending'
original.line_items.parents_only.non_shipping.each do |li|
li_attrs = li.attributes.dup.symbolize_keys
li_attrs = li_attrs.except(:id, :resource_type, :resource_id,
:delivery_id, :tax_total, :tax_rate,
:tax_type, :resource_tax_rate_id,
:original_delivery_id, :children_count, :parent_id)
li_attrs[:original_delivery_id] = li[:delivery_id]
if li_attrs[:discounted_price].to_f.zero? && li_attrs[:price].to_f.positive?
li_attrs[:discounted_price] = li_attrs[:price]
end
copy.line_items.new(li_attrs)
end
end
end
|
#dependents(limit: 5) ⇒ Object
A quote with communications to a customer cannot be deleted
A quote used as a parent of another quote is also restricted (clear the children first)
A quote with orders cannot be deleted
757
758
759
760
761
762
763
|
# File 'app/models/quote.rb', line 757
def dependents(limit: 5)
{
orders: orders.limit(limit),
children: children.limit(limit),
communications: communications.limit(limit)
}
end
|
#editing_locked? ⇒ Boolean
657
658
659
|
# File 'app/models/quote.rb', line 657
def editing_locked?
cancelled? || complete?
end
|
#effective_date_for_coupon ⇒ Object
456
457
458
|
# File 'app/models/quote.rb', line 456
def effective_date_for_coupon
override_coupon_date.presence || complete_datetime.try(:to_date) || created_at.try(:to_date) || Date.current
end
|
#exclude_manually_initiated_event?(_event) ⇒ Boolean
436
437
438
|
# File 'app/models/quote.rb', line 436
def exclude_manually_initiated_event?(_event)
false
end
|
564
565
566
|
# File 'app/models/quote.rb', line 564
def (with_extension = true)
"quote_#{reference_number}_plans#{'.pdf' if with_extension}"
end
|
#file_name(with_extension = true) ⇒ Object
560
561
562
|
# File 'app/models/quote.rb', line 560
def file_name(with_extension = true)
"quote_#{reference_number}#{'.pdf' if with_extension}"
end
|
#get_next_reference_number ⇒ Object
889
890
891
892
893
894
895
896
|
# File 'app/models/quote.rb', line 889
def get_next_reference_number
ref_base = begin
self.class.connection.execute("SELECT nextval('quotes_reference_numbers_seq') AS reference_number").first['reference_number']
rescue StandardError
nil
end
"#{quote_type}#{ref_base}"
end
|
#goods_product_line_ids ⇒ Object
409
410
411
|
# File 'app/models/quote.rb', line 409
def goods_product_line_ids
line_items.goods.joins(:item).where.not(Item[:primary_product_line_id].eq(nil)).pluck(Arel.sql('distinct items.primary_product_line_id'))
end
|
#has_discounts? ⇒ Boolean
578
579
580
|
# File 'app/models/quote.rb', line 578
def has_discounts?
line_items.any?(&:is_discounted?)
end
|
#has_no_instant_quoting_rooms ⇒ Object
906
907
908
909
|
# File 'app/models/quote.rb', line 906
def has_no_instant_quoting_rooms
true
end
|
#has_onsite_smartsupport? ⇒ Boolean
967
968
969
970
971
|
# File 'app/models/quote.rb', line 967
def has_onsite_smartsupport?
return true if service_distance.present? && (service_distance < SMART_SERVICES_MAX_DISTANCE)
false
end
|
#has_pre_pack_deliveries? ⇒ Boolean
1018
1019
1020
|
# File 'app/models/quote.rb', line 1018
def has_pre_pack_deliveries?
pre_pack? || deliveries.any?(&:pre_pack?)
end
|
#has_selected_heated_items? ⇒ Boolean
973
974
975
976
977
978
979
980
981
|
# File 'app/models/quote.rb', line 973
def has_selected_heated_items?
if customer.is_homeowner?
smartinstall_data.present?
elsif customer.is_dealer_or_trade_pro_for_locator?
smartsupport_info.present?
else
false
end
end
|
#installation_country_iso ⇒ Object
Alias for Opportunity#installation_country_iso
154
|
# File 'app/models/quote.rb', line 154
delegate :installation_postal_code, :installation_state_code, :installation_country_iso, :installation_country_iso3, to: :opportunity
|
#installation_country_iso3 ⇒ Object
Alias for Opportunity#installation_country_iso3
154
|
# File 'app/models/quote.rb', line 154
delegate :installation_postal_code, :installation_state_code, :installation_country_iso, :installation_country_iso3, to: :opportunity
|
#installation_is_within_range? ⇒ Boolean
983
984
985
|
# File 'app/models/quote.rb', line 983
def installation_is_within_range?
service_distance.present? && (service_distance <= SMART_SERVICES_MAX_DISTANCE) end
|
#installation_postal_code ⇒ Object
Alias for Opportunity#installation_postal_code
154
|
# File 'app/models/quote.rb', line 154
delegate :installation_postal_code, :installation_state_code, :installation_country_iso, :installation_country_iso3, to: :opportunity
|
#installation_state_code ⇒ Object
Alias for Opportunity#installation_state_code
154
|
# File 'app/models/quote.rb', line 154
delegate :installation_postal_code, :installation_state_code, :installation_country_iso, :installation_country_iso3, to: :opportunity
|
#insulation_sq_ft ⇒ Object
815
816
817
818
819
820
821
|
# File 'app/models/quote.rb', line 815
def insulation_sq_ft
sqft = 0
room_configurations.each do |rc|
sqft += rc.insulation_surface.to_i if rc.room_type&.is_indoor? && (rc.insulation_surface.to_i > 0)
end
sqft
end
|
#is_first_quote? ⇒ Boolean
807
808
809
|
# File 'app/models/quote.rb', line 807
def is_first_quote?
parent_id.nil?
end
|
#is_revision? ⇒ Boolean
811
812
813
|
# File 'app/models/quote.rb', line 811
def is_revision?
parent_id.present?
end
|
#is_sales_quote? ⇒ Boolean
803
804
805
|
# File 'app/models/quote.rb', line 803
def is_sales_quote?
quote_type == SALES_QUOTE
end
|
#is_smart_service_quote? ⇒ Boolean
987
988
989
|
# File 'app/models/quote.rb', line 987
def is_smart_service_quote?
line_items.non_shipping.all?(&:is_smart_service?)
end
|
#linked_support_cases ⇒ ActiveRecord::Relation<LinkedSupportCase>
167
|
# File 'app/models/quote.rb', line 167
has_many :linked_support_cases, through: :room_configurations, source: :support_cases
|
#local_sales_rep ⇒ Object
Alias for Customer#local_sales_rep
153
|
# File 'app/models/quote.rb', line 153
delegate :primary_sales_rep, :secondary_sales_rep, :local_sales_rep, to: :customer
|
#lookup_link ⇒ Object
1037
1038
1039
|
# File 'app/models/quote.rb', line 1037
def lookup_link
"https://#{WEB_HOSTNAME}/quote-lookup?quote_id=#{id}"
end
|
#main_rep ⇒ Object
625
626
627
|
# File 'app/models/quote.rb', line 625
def main_rep
primary_sales_rep
end
|
#material_alerts ⇒ ActiveRecord::Relation<MaterialAlert>
168
|
# File 'app/models/quote.rb', line 168
has_many :material_alerts, dependent: :destroy
|
#messaging_logs ⇒ ActiveRecord::Relation<MessagingLog>
166
|
# File 'app/models/quote.rb', line 166
has_many :messaging_logs, dependent: :destroy, as: :resource
|
#name ⇒ Object
722
723
724
|
# File 'app/models/quote.rb', line 722
def name
"Quote ##{reference_number}"
end
|
#ok_to_customer_cancel? ⇒ Boolean
769
770
771
|
# File 'app/models/quote.rb', line 769
def ok_to_customer_cancel?
CUSTOMER_CANCELABLE_STATES.include?(state.to_sym) && orders.not_cancelled.empty?
end
|
#ok_to_delete? ⇒ Boolean
765
766
767
|
# File 'app/models/quote.rb', line 765
def ok_to_delete?
dependents.values.flatten.empty?
end
|
#open_activities_counter ⇒ Object
1005
1006
1007
|
# File 'app/models/quote.rb', line 1005
def open_activities_counter
all_activities.open_activities.visible_by_default.size
end
|
156
|
# File 'app/models/quote.rb', line 156
belongs_to :opportunity, optional: true
|
#orders ⇒ ActiveRecord::Relation<Order>
161
|
# File 'app/models/quote.rb', line 161
has_many :orders
|
#participants_options_for_select ⇒ Object
940
941
942
|
# File 'app/models/quote.rb', line 940
def participants_options_for_select
opportunity&.opportunity_participants&.map { |scp| [scp.party.full_name, scp.party_id] }
end
|
#post_communication_queued_hook(_params = {}) ⇒ Object
527
528
529
530
|
# File 'app/models/quote.rb', line 527
def post_communication_queued_hook(_params = {})
complete if can_complete?
end
|
#post_communication_sent_hook(_params = {}) ⇒ Object
532
533
534
535
|
# File 'app/models/quote.rb', line 532
def post_communication_sent_hook(_params = {})
complete if can_complete?
end
|
#pre_packable? ⇒ Boolean
777
778
779
|
# File 'app/models/quote.rb', line 777
def pre_packable?
deliveries.any? && deliveries.any? { |d| d.shipments.any? { |s| !s.packed_or_pre_packed? } }
end
|
#prefix_for_reference_number ⇒ Object
902
903
904
|
# File 'app/models/quote.rb', line 902
def prefix_for_reference_number
"#{opportunity.opportunity_type}Q"
end
|
#prepared_for_name ⇒ Object
789
790
791
|
# File 'app/models/quote.rb', line 789
def prepared_for_name
(contact || customer).name
end
|
#prevent_recalculate_shipping? ⇒ Boolean
911
912
913
|
# File 'app/models/quote.rb', line 911
def prevent_recalculate_shipping?
%i[complete cancelled].include?(state.to_sym) || retrieving_shipping_costs?
end
|
#pricing_program_description ⇒ Object
923
924
925
|
# File 'app/models/quote.rb', line 923
def pricing_program_description
tier2_program_pricing_coupon.try(:title) || 'MSRP/Catalog'
end
|
#pricing_program_discount ⇒ Object
927
928
929
|
# File 'app/models/quote.rb', line 927
def pricing_program_discount
(tier2_program_pricing_coupon.try(:amount_goods).presence || 0).to_i
end
|
#primary_party ⇒ Object
Alias for Opportunity#primary_party
149
|
# File 'app/models/quote.rb', line 149
delegate :customer, :contact, :primary_party, to: :opportunity
|
#primary_sales_rep ⇒ Object
Alias for Customer#primary_sales_rep
153
|
# File 'app/models/quote.rb', line 153
delegate :primary_sales_rep, :secondary_sales_rep, :local_sales_rep, to: :customer
|
#ready_to_complete? ⇒ Boolean
568
569
570
571
572
|
# File 'app/models/quote.rb', line 568
def ready_to_complete?
all_rooms_complete_or_cancelled_or_draft?
end
|
#recipient_email ⇒ Object
485
486
487
488
489
|
# File 'app/models/quote.rb', line 485
def recipient_email
contact_points.emails.first&.detail ||
primary_party&.email ||
customer&.email
end
|
#recipient_name ⇒ Object
481
482
483
|
# File 'app/models/quote.rb', line 481
def recipient_name
primary_party&.name || shipping_address&.person_name || customer&.name
end
|
#reference_number_with_name ⇒ Object
718
719
720
|
# File 'app/models/quote.rb', line 718
def reference_number_with_name
[reference_number, suffix].compact.join(' - ')
end
|
#reference_number_with_opp_name ⇒ Object
726
727
728
|
# File 'app/models/quote.rb', line 726
def reference_number_with_opp_name
"#{reference_number} #{opportunity.try(:name)}"
end
|
#reference_number_with_opp_name_when_specified ⇒ Object
730
731
732
733
734
|
# File 'app/models/quote.rb', line 730
def reference_number_with_opp_name_when_specified
s = "#{reference_number}"
s << " #{opportunity.name}" unless opportunity.nil? || opportunity.unknown_job_name?
s
end
|
#request_plans_or_complete ⇒ Object
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
|
# File 'app/models/quote.rb', line 629
def request_plans_or_complete
return if complete? || cancelled?
self.do_not_set_totals = true
log_prefix = "Quote(#{id}):request_plans_or_complete"
if room_configurations.empty? && line_items.present?
logger.info "#{log_prefix} no rooms but line present. attempting ready to transmit"
ready_to_transmit! if can_ready_to_transmit?
elsif can_installation_plans_complete?
logger.info "#{log_prefix} installation plan can complete, trying"
installation_plans_complete!
elsif can_ready_for_design?
logger.info "#{log_prefix} can ready for design, trying"
ready_for_design!
elsif can_pending_project_details?
logger.info "#{log_prefix} no line items and nothing else happening, go back to draft"
pending_project_details!
else
logger.info "#{log_prefix} reverting to draft instant quoting rooms"
end
end
|
#reset_discount_on_ships_economy_change ⇒ Object
1070
1071
1072
1073
1074
1075
|
# File 'app/models/quote.rb', line 1070
def reset_discount_on_ships_economy_change
return unless saved_change_to_ships_economy?
reset_discount(reset_item_pricing: false)
true
end
|
#rma ⇒ Rma
158
|
# File 'app/models/quote.rb', line 158
belongs_to :rma, optional: true
|
#schedule_follow_up(follow_up_task_type: nil) ⇒ Object
Schedule follow up on quote
876
877
878
879
880
881
882
883
884
885
886
887
|
# File 'app/models/quote.rb', line 876
def schedule_follow_up(follow_up_task_type: nil)
return if customer.guest? && !opportunity.emailable? && !opportunity.voice_callable?
return if opportunity.activities.quofu.open_activities.present?
opportunity.cancel_oppfu
opportunity.create_follow_up_activity(follow_up_task_type:)
end
|
#secondary_sales_rep ⇒ Object
Alias for Customer#secondary_sales_rep
153
|
# File 'app/models/quote.rb', line 153
delegate :primary_sales_rep, :secondary_sales_rep, :local_sales_rep, to: :customer
|
#selection_name ⇒ Object
831
832
833
|
# File 'app/models/quote.rb', line 831
def selection_name
reference_number_with_name
end
|
#send_profit_review_notification ⇒ Object
#sender ⇒ Object
793
794
795
796
797
798
799
800
801
|
# File 'app/models/quote.rb', line 793
def sender
if primary_sales_rep
primary_sales_rep
elsif opportunity.primary_sales_rep
opportunity.primary_sales_rep
elsif customer.primary_sales_rep
customer.primary_sales_rep
end
end
|
#service_distance ⇒ Object
991
992
993
994
|
# File 'app/models/quote.rb', line 991
def service_distance
zip_code = opportunity.installation_postal_code
SmartServicesController.helpers.calculate_distance_from_lz(zip_code)
end
|
#set_currency ⇒ Object
680
681
682
683
684
685
686
687
|
# File 'app/models/quote.rb', line 680
def set_currency
store_currency = begin
opportunity.customer.store.currency
rescue StandardError
nil
end
self.currency = store_currency if store_currency
end
|
#set_default_quote_type ⇒ Object
1063
1064
1065
1066
1067
1068
|
# File 'app/models/quote.rb', line 1063
def set_default_quote_type
self.quote_type ||= prefix_for_reference_number
return unless quote_type_changed? && reference_number.present?
self.reference_number = "#{quote_type}#{reference_number[2..50]}"
end
|
#set_expiration_date ⇒ Object
1049
1050
1051
1052
1053
|
# File 'app/models/quote.rb', line 1049
def set_expiration_date
return if complete?
self.expiration_date = calculate_expiration_date
end
|
#set_min_profit_markup ⇒ Object
1055
1056
1057
1058
1059
1060
1061
|
# File 'app/models/quote.rb', line 1055
def set_min_profit_markup
self.min_profit_markup = if quote_type == 'SQ'
default_sales_markup
else
0
end
end
|
#set_opportunity_value ⇒ Object
823
824
825
|
# File 'app/models/quote.rb', line 823
def set_opportunity_value
opportunity&.calculate_value
end
|
#set_priority(save = true) ⇒ Object
689
690
691
692
693
694
695
696
697
698
699
700
|
# File 'app/models/quote.rb', line 689
def set_priority(save = true)
highest_priority = begin
RoomConfiguration::PRIORITIES[room_configurations.map(&:priority_urgency_factor).min]
rescue StandardError
'standard'
end
if save
update_attribute(:priority, highest_priority)
else
self.priority = highest_priority
end
end
|
#set_reference_number ⇒ Object
898
899
900
|
# File 'app/models/quote.rb', line 898
def set_reference_number
self.reference_number ||= get_next_reference_number
end
|
#shipping_address_nil_or_valid? ⇒ Boolean
574
575
576
|
# File 'app/models/quote.rb', line 574
def shipping_address_nil_or_valid?
shipping_address.nil? || shipping_address.valid?
end
|
#smartsupport_info ⇒ Object
961
962
963
964
965
|
# File 'app/models/quote.rb', line 961
def smartsupport_info
return nil if service_distance.nil?
smartsupport_data(service_distance)
end
|
#sms_enabled_numbers ⇒ Object
539
540
541
|
# File 'app/models/quote.rb', line 539
def sms_enabled_numbers
ContactPoint.joins(:party).merge(all_participants).sms_numbers.order(:detail).map(&:formatted_for_sms).uniq
end
|
#sms_messages ⇒ Object
543
544
545
|
# File 'app/models/quote.rb', line 543
def sms_messages
SmsMessage.for_numbers(sms_enabled_numbers)
end
|
#sold? ⇒ Boolean
750
751
752
|
# File 'app/models/quote.rb', line 750
def sold?
orders.non_carts.active.present?
end
|
#special_ordering_instructions ⇒ Object
674
675
676
677
678
|
# File 'app/models/quote.rb', line 674
def special_ordering_instructions
customer.billing_address.party.ordering_instructions
rescue StandardError
nil
end
|
#stop_for_pre_pack? ⇒ Boolean
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
|
# File 'app/models/quote.rb', line 1022
def stop_for_pre_pack?
return false unless can_request_estimated_packaging?
return false if shipping_address.nil?
return false if customer.is_e_commerce_misc?
return false if line_items.empty?
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?) || d.is_smart_service? }
need_to_pre_pack_reasons.any?
end
|
#stop_for_profit_review? ⇒ Boolean
1009
1010
1011
1012
1013
1014
1015
1016
|
# File 'app/models/quote.rb', line 1009
def stop_for_profit_review?
return false if customer.is_e_commerce_misc?
return false if line_items.empty?
return false if room_configurations.present? && !room_configurations.all?(&:transmittable?)
!profit_margins_met?
end
|
#store ⇒ Object
150
|
# File 'app/models/quote.rb', line 150
delegate :store, to: :customer
|
#support_cases ⇒ ActiveRecord::Relation<SupportCase>
171
|
# File 'app/models/quote.rb', line 171
has_and_belongs_to_many :support_cases
|
#tier2_program_pricing ⇒ Object
915
916
917
|
# File 'app/models/quote.rb', line 915
def tier2_program_pricing
discounts.tier2.first
end
|
#tier2_program_pricing_coupon ⇒ Object
919
920
921
|
# File 'app/models/quote.rb', line 919
def tier2_program_pricing_coupon
tier2_program_pricing.coupon if tier2_program_pricing
end
|
#to_liquid ⇒ Object
405
406
407
|
# File 'app/models/quote.rb', line 405
def to_liquid
Liquid::QuoteDrop.new self
end
|
#to_order(order = nil, txid = nil) ⇒ Object
746
747
748
|
# File 'app/models/quote.rb', line 746
def to_order(order = nil, txid = nil)
convert_to_order_service.convert(order, txid)
end
|
#to_s ⇒ Object
827
828
829
|
# File 'app/models/quote.rb', line 827
def to_s
"Quote # #{reference_number_with_name}"
end
|
#total_amps ⇒ Object
1000
1001
1002
|
# File 'app/models/quote.rb', line 1000
def total_amps
room_configurations.to_a.sum { |rc| rc.calculate_total_amps.round(2) }&.round(2)
end
|
#track_profit? ⇒ Boolean
996
997
998
|
# File 'app/models/quote.rb', line 996
def track_profit?
profitable_line_items.present? && is_sales_quote?
end
|
#tracking_email_address ⇒ Object
835
836
837
838
839
840
|
# File 'app/models/quote.rb', line 835
def tracking_email_address
domain = Rails.application.config.x.email_domain
prefix = 'quo'
encrypted_id = Encryption.encrypt_string(id.to_s)
"#{prefix}+id#{encrypted_id}@#{domain}"
end
|
#uploads ⇒ ActiveRecord::Relation<Upload>
163
|
# File 'app/models/quote.rb', line 163
has_many :uploads, as: :resource, dependent: :destroy
|
#versions_for_audit_trail(_params = {}) ⇒ Object
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
|
# File 'app/models/quote.rb', line 460
def versions_for_audit_trail(_params = {})
query_sql = %q{
(item_type = 'Quote' and item_id = :id)
OR (
item_type = 'LineItem'
AND reference_data @> '{"resource_type": "Quote"}'
AND reference_data @> '{"resource_id": :id}'
)
OR (
item_type = 'Delivery'
AND reference_data @> '{"quote_id": :id}'
)
OR (
item_type = 'Discount'
AND reference_data @> '{"itemizable_id": :id}'
AND reference_data @> '{"itemizable_type": "Quote"}'
)
}
RecordVersion.where(query_sql, id:)
end
|
159
|
# File 'app/models/quote.rb', line 159
belongs_to :visit, optional: true
|
#will_complete? ⇒ Boolean
491
492
493
494
495
496
|
# File 'app/models/quote.rb', line 491
def will_complete?
line_items.any? &&
shipping_address_nil_or_valid? &&
all_rooms_complete_or_cancelled_or_draft? &&
profit_margins_met?
end
|
#will_installation_plans_complete? ⇒ Boolean
498
499
500
|
# File 'app/models/quote.rb', line 498
def will_installation_plans_complete?
room_configurations.exists? && all_rooms_complete_or_cancelled_or_draft? && line_items.present?
end
|
#will_pending_project_details? ⇒ Boolean
518
519
520
521
|
# File 'app/models/quote.rb', line 518
def will_pending_project_details?
awaiting_completed_installation_plans? ||
(line_items.empty? && room_configurations.any?(&:draft?))
end
|
#will_ready_for_design? ⇒ Boolean
502
503
504
|
# File 'app/models/quote.rb', line 502
def will_ready_for_design?
room_configurations.exists? && any_rooms_in_design?
end
|
#will_ready_to_transmit? ⇒ Boolean
506
507
508
509
510
511
512
|
# File 'app/models/quote.rb', line 506
def will_ready_to_transmit?
line_items.any? &&
shipping_address_nil_or_valid? &&
profit_margins_met? &&
!stop_for_pre_pack? &&
(room_configurations.empty? || room_configurations.all?(&:transmittable?))
end
|
#zones ⇒ Object
620
621
622
623
|
# File 'app/models/quote.rb', line 620
def zones
end
|