Class: HeatingSystemCalculator::HeatingSystemItems

Inherits:
Object
  • Object
show all
Defined in:
app/services/heating_system_calculator/heating_system_items.rb

Overview

Service object: heating system items.

Constant Summary collapse

JUNCTION_BOX_COLD_LEAD_MAX_NUMBERS =

Junction box cold lead max numbers.

[3, 8, 8].freeze
SCREWS_PER_STRIP_WITH_OVERAGE =

TZ Cable concrete subfloor installation constants
Screws per strip with 10% overage factor

2.2
TAPE_FEET_PER_STRIP_WITH_OVERAGE =

Tape feet per strip with 10% overage factor

1.1
CONCRETE_SCREW_PACK_LARGE_SIZE =

Large screw pack contains 100 screws (includes drill bit)

100
CONCRETE_SCREW_PACK_SMALL_SIZE =

Small screw pack contains 20 screws

20
TZ_CABLE_TAPE_ROLL_LENGTH_FT =

Double-sided tape roll length in feet

26
SQUARE_INCHES_PER_SQUARE_FOOT =

Unit conversion: square inches per square foot (12 * 12)

144.0
FLOORWARMING_TSTAT_SORT_ORDER_BY_SKU =

Floorwarming tstat sort order by sku.

{
  'UTN5-4999' => 0,
  'UDG5-4999-WY' => 1,
  'UDG4-4999' => 2,
  'UWG4-4999' => 3
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ HeatingSystemItems

Returns a new instance of HeatingSystemItems.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 34

def initialize(options = {})
  @heating_system = options[:heating_system]
  @error = { error_status: :missing_heating_system, error_message: 'Heating System is missing.' } if @heating_system.nil?
  @heated_area = options[:heated_area]
  @error = { error_status: :missing_heated_area, error_message: 'Heated Area is missing.' } if @heated_area.nil?
  return if @error

  @element_set = get_element_set
  @error = { error_status: :no_matching_elements, error_message: 'No matching heating elements.' } if @element_set.empty?
  @control_set = get_control_set
  @error = { error_status: :no_matching_controls, error_message: 'No matching controls.' } if @control_set.empty?
  @power_set = get_power_set
  @sensor_set = get_sensor_set
  @accessory_set = get_accessory_set
  @underlayment_set = get_underlayment_set
  @rough_in_kit_for_tstat = get_rough_in_kit_for_tstat
  @rough_in_kit_for_power_module = get_rough_in_kit_for_power_module
  @smart_service_set = get_smart_service_set
end

Instance Attribute Details

#accessory_setObject (readonly)

Returns the value of attribute accessory_set.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def accessory_set
  @accessory_set
end

#control_setObject (readonly)

Returns the value of attribute control_set.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def control_set
  @control_set
end

#element_setObject (readonly)

Returns the value of attribute element_set.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def element_set
  @element_set
end

#errorObject (readonly)

Returns the value of attribute error.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def error
  @error
end

#power_setObject (readonly)

Returns the value of attribute power_set.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def power_set
  @power_set
end

#rough_in_kit_for_power_moduleObject (readonly)

Returns the value of attribute rough_in_kit_for_power_module.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def rough_in_kit_for_power_module
  @rough_in_kit_for_power_module
end

#rough_in_kit_for_tstatObject (readonly)

Returns the value of attribute rough_in_kit_for_tstat.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def rough_in_kit_for_tstat
  @rough_in_kit_for_tstat
end

#sensor_setObject (readonly)

Returns the value of attribute sensor_set.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def sensor_set
  @sensor_set
end

#smart_service_setObject (readonly)

Returns the value of attribute smart_service_set.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def smart_service_set
  @smart_service_set
end

#underlayment_setObject (readonly)

Returns the value of attribute underlayment_set.



31
32
33
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 31

def underlayment_set
  @underlayment_set
end

Instance Method Details

#floor_warming_tstat_sort_order_for_hash(item) ⇒ Object

==========================================================================
Sorting Helpers



233
234
235
236
237
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 233

def floor_warming_tstat_sort_order_for_hash(item)
  sort_order = FLOORWARMING_TSTAT_SORT_ORDER_BY_SKU[item['sku']]
  sort_order ||= item['price']
  sort_order
end

#get_accessory_setObject

==========================================================================
Accessory Retrieval



134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 134

def get_accessory_set
  # Return empty if no product line selected (e.g., step 0)
  return [] if @heating_system.heating_system_product_line.blank?

  # Snow melt has complex junction box handling - use AR
  return get_accessory_set_snow_melt if @heating_system.heating_system_product_line.snow_melt?

  # Standard path: use materialized view
  ViewQuoteBomItem.get_accessories(
    catalog_id: @heating_system.catalog.id,
    product_line_id: @heating_system.heating_system_product_line.id
  )
end

#get_accessory_set_snow_meltObject



219
220
221
222
223
224
225
226
227
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 219

def get_accessory_set_snow_melt
  # Use matview for accessories + junction boxes
  accessories = ViewQuoteBomItem.get_accessories(
    catalog_id: @heating_system.catalog.id,
    product_line_id: @heating_system.heating_system_product_line.id
  )
  junction_boxes = ViewQuoteBomItem.get_junction_boxes(catalog_id: @heating_system.catalog.id)
  accessories + junction_boxes
end

#get_control_setObject

==========================================================================
Control Retrieval



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 78

def get_control_set
  # Return empty if no product line selected (e.g., step 0)
  return [] if @heating_system.heating_system_product_line.blank?

  # Snow melt has complex successor item logic - use AR
  return get_control_set_snow_melt if @heating_system.heating_system_product_line.snow_melt?

  # Standard path: use materialized view
  ViewQuoteBomItem.get_controls(
    catalog_id: @heating_system.catalog.id,
    product_line_id: @heating_system.heating_system_product_line.id
  ).sort_by { |item| floor_warming_tstat_sort_order_for_hash(item) }
end

#get_control_set_snow_meltObject

==========================================================================
AR Fallback Methods (for snow melt successor item logic)



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 179

def get_control_set_snow_melt
  catalog_id = @heating_system.catalog.id

  # Step 1: Filter SKUs based on business rules (no DB queries)
  candidate_skus = ItemConstants::SNOWMELT_CONTROL_BUNDLES.filter_map do |bundle|
    next if bundle[:name] == 'Economy' && @heating_system.floor_type.seo_key == 'paver'
    next if bundle[:name] == 'Value' && @heating_system.voltage&.id.to_i > 240

    bundle[:sku]
  end

  # Step 2: Batch-check which SKUs exist in matview (single query)
  existing_skus = ViewQuoteBomItem.for_catalog(catalog_id).where(sku: candidate_skus).pluck(:sku)
  missing_skus = candidate_skus - existing_skus

  # Step 3: For missing SKUs, batch-check for successors (only if any missing)
  # Track mapping from successor SKU to original bundle SKU for relay panel logic
  successor_to_original = {}
  if missing_skus.any?
    # Load all potentially missing catalog items with associations in one query
    missing_items = CatalogItem.includes(store_item: { item: :successor_item })
                               .joins(store_item: :item)
                               .where(catalog_id: catalog_id, items: { sku: missing_skus })

    missing_items.each do |ci|
      original_sku = ci.item&.sku
      if ci.item&.successor_item.present? && (sci = ci.get_successor_online_catalog_item).present?
        successor_to_original[sci.sku] = original_sku
      end
    end
  end

  # Use matview for final fetch, passing the successor mapping
  ViewQuoteBomItem.get_snow_melt_controls(
    catalog_id: catalog_id,
    skus: existing_skus + successor_to_original.keys,
    successor_to_original: successor_to_original
  )
end

#get_element_setObject

==========================================================================
Element Retrieval - Uses materialized view for optimal performance



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 58

def get_element_set
  # Return empty if no product line selected (e.g., step 0)
  return [] if @heating_system.heating_system_product_line.blank?

  # Tire tracks legacy filters to 24" wide elements only
  width_filter = @heated_area.coverage_state == 'tire_tracks_legacy' ? 24.0 : nil

  ViewQuoteBomItem.get_elements(
    catalog_id: @heating_system.catalog.id,
    product_line_url: @heating_system.heating_system_product_line.slug_ltree.to_s,
    voltage_id: @heating_system.voltage&.id,
    cable_spacing: @heating_system.cable_spacing,
    width_filter: width_filter
  )
end

#get_power_setObject

==========================================================================
Power Module Retrieval



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 96

def get_power_set
  # Return empty if no product line selected (e.g., step 0)
  return [] if @heating_system.heating_system_product_line.blank?

  # Snow melt uses specific relay panel SKUs
  return ViewQuoteBomItem.get_relay_panels(catalog_id: @heating_system.catalog.id) if @heating_system.heating_system_product_line.snow_melt?

  # Standard path: use materialized view
  power_items = ViewQuoteBomItem.get_power(
    catalog_id: @heating_system.catalog.id,
    product_line_id: @heating_system.heating_system_product_line.id
  )
  relay_panels = ViewQuoteBomItem.get_relay_panels(catalog_id: @heating_system.catalog.id)
  power_items + relay_panels
end

#get_rough_in_kit_for_power_moduleObject



164
165
166
167
168
169
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 164

def get_rough_in_kit_for_power_module
  ViewQuoteBomItem.get_rough_in_kits(
    catalog_id: @heating_system.catalog.id,
    skus: [ItemConstants::ROUGH_IN_KIT_FOR_POWER_MODULE_SKU]
  ).first
end

#get_rough_in_kit_for_tstatObject



157
158
159
160
161
162
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 157

def get_rough_in_kit_for_tstat
  ViewQuoteBomItem.get_rough_in_kits(
    catalog_id: @heating_system.catalog.id,
    skus: [ItemConstants::ROUGH_IN_KIT_FOR_TSTAT_SKU]
  ).first
end

#get_sensor_setObject

==========================================================================
Sensor Retrieval



116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 116

def get_sensor_set
  # Return empty if no product line selected (e.g., step 0)
  return [] if @heating_system.heating_system_product_line.blank?

  # Snow melt uses specific sensor SKUs
  return ViewQuoteBomItem.get_snow_melt_sensors(catalog_id: @heating_system.catalog.id) if @heating_system.heating_system_product_line.snow_melt?

  # Standard path: use materialized view
  ViewQuoteBomItem.get_sensors(
    catalog_id: @heating_system.catalog.id,
    product_line_id: @heating_system.heating_system_product_line.id
  )
end

#get_smart_service_setObject



171
172
173
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 171

def get_smart_service_set
  ViewQuoteBomItem.get_smart_services(catalog_id: @heating_system.catalog.id)
end

#get_underlayment_setObject

==========================================================================
Underlayment, Rough-in Kits, and Smart Services
These always use AR due to special handling requirements



153
154
155
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 153

def get_underlayment_set
  ViewQuoteBomItem.get_underlayments(catalog_id: @heating_system.catalog.id)
end

#line_items_to_bom_controls(line_item_elements) ⇒ Object



248
249
250
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 248

def line_items_to_bom_controls(line_item_elements)
  ViewQuoteBomItem.line_items_to_bom(line_item_elements)
end

#line_items_to_bom_elements(line_item_elements) ⇒ Object

==========================================================================
Line Item Converters (used by RoomConfiguration model)
Uses matview for efficient batch lookup



244
245
246
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 244

def line_items_to_bom_elements(line_item_elements)
  ViewQuoteBomItem.line_items_to_bom(line_item_elements, cable_spacing: @heating_system&.cable_spacing)
end