Class: HeatingSystemCalculator::HeatingSystemItems

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

Constant Summary collapse

JUNCTION_BOX_COLD_LEAD_MAX_NUMBERS =
[3, 8, 8]
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 =
{
  'UTN5-4999' => 0,
  'UDG5-4999-WY' => 1,
  'UDG4-4999' => 2,
  'UWG4-4999' => 3
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ HeatingSystemItems

Returns a new instance of HeatingSystemItems.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 30

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.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def accessory_set
  @accessory_set
end

#control_setObject (readonly)

Returns the value of attribute control_set.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def control_set
  @control_set
end

#element_setObject (readonly)

Returns the value of attribute element_set.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def element_set
  @element_set
end

#errorObject (readonly)

Returns the value of attribute error.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def error
  @error
end

#power_setObject (readonly)

Returns the value of attribute power_set.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def power_set
  @power_set
end

#rough_in_kit_for_power_moduleObject (readonly)

Returns the value of attribute rough_in_kit_for_power_module.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

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.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def rough_in_kit_for_tstat
  @rough_in_kit_for_tstat
end

#sensor_setObject (readonly)

Returns the value of attribute sensor_set.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def sensor_set
  @sensor_set
end

#smart_service_setObject (readonly)

Returns the value of attribute smart_service_set.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def smart_service_set
  @smart_service_set
end

#underlayment_setObject (readonly)

Returns the value of attribute underlayment_set.



27
28
29
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 27

def underlayment_set
  @underlayment_set
end

Instance Method Details

#floor_warming_tstat_sort_order_for_hash(item) ⇒ Object

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



229
230
231
232
233
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 229

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



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 130

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



215
216
217
218
219
220
221
222
223
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 215

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



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 74

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)



175
176
177
178
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
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 175

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



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 54

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



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 92

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



160
161
162
163
164
165
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 160

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



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

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



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 112

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



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

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



149
150
151
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 149

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

#line_items_to_bom_controls(line_item_elements) ⇒ Object



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

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



240
241
242
# File 'app/services/heating_system_calculator/heating_system_items.rb', line 240

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