Class: HeatingSystemCalculator::AccessoriesFinder
- Inherits:
-
BaseItemFinder
- Object
- BaseItemFinder
- HeatingSystemCalculator::AccessoriesFinder
- Defined in:
- app/services/heating_system_calculator/accessories_finder.rb
Constant Summary collapse
- FREEFORM_CABLE_ACCESSORY_SKUS =
SKUs for free-form cable accessories that require no membrane and existing concrete subfloor
[ ItemConstants::TZ_CABLE_GRIPSTRIP_SKU, ItemConstants::TZ_CABLE_STRIP_PL_SKU, ItemConstants::TZ_CABLE_TAPE_SKU, ItemConstants::TZ_CABLE_CONCRETE_SCREW_LARGE_SKU, ItemConstants::TZ_CABLE_CONCRETE_SCREW_SMALL_SKU, ItemConstants::TZ_CABLE_CONCRETE_DRILL_BIT_SKU ].freeze
Instance Attribute Summary collapse
-
#accessories ⇒ Object
readonly
Returns the value of attribute accessories.
-
#error ⇒ Object
readonly
Returns the value of attribute error.
Instance Method Summary collapse
- #calculate_elements_qty_and_tz_cable_accessories_qty(elements) ⇒ Object
- #calculate_tstat_qty_and_pm_control_qty(controls) ⇒ Object
- #find_accessories(elements, controls) ⇒ Object
-
#initialize(options) ⇒ AccessoriesFinder
constructor
A new instance of AccessoriesFinder.
- #select_floor_heating_accessories(elements, controls) ⇒ Object
- #select_snow_melting_accessories(elements, _controls) ⇒ Object
- #select_underlayment(_elements, _controls) ⇒ Object
Methods inherited from BaseItemFinder
#get_number_of_poles_from_consolidated_elements, #get_total_from_solution
Constructor Details
#initialize(options) ⇒ AccessoriesFinder
Returns a new instance of AccessoriesFinder.
7 8 9 10 11 12 13 14 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 7 def initialize() @heated_area = [:heated_area] @heating_system = [:heating_system] @accessory_set = [:accessory_set] @underlayment_set = [:underlayment_set] @rough_in_kit_for_tstat = [:rough_in_kit_for_tstat] @rough_in_kit_for_power_module = [:rough_in_kit_for_power_module] end |
Instance Attribute Details
#accessories ⇒ Object (readonly)
Returns the value of attribute accessories.
5 6 7 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 5 def accessories @accessories end |
#error ⇒ Object (readonly)
Returns the value of attribute error.
5 6 7 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 5 def error @error end |
Instance Method Details
#calculate_elements_qty_and_tz_cable_accessories_qty(elements) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 116 def calculate_elements_qty_and_tz_cable_accessories_qty(elements) # decide on quantities, generally one per heating element except for TZ cable guides qty = 0 tz_cable_accessories_qtys = {} # decide if we find tz cable strips and related accessories find_tz_cable_strips = RoomConfiguration::PUBLIC_TZ_CABLE_SPACINGS.include?(@heating_system.cable_spacing) && !@heating_system.membrane_recommended ? true : false # Batch load all element items upfront to avoid N+1 queries element_skus = elements.map { |e| e['sku'] }.compact items_by_sku = Item.where(sku: element_skus).index_by(&:sku) elements.each do |e| qty += e['qty'] || 1 next unless @heating_system.heating_system_product_line.is_cable_system? item = items_by_sku[e['sku']] next unless item && (item.is_tz_thin_cable? || item.is_tz_cable?) && find_tz_cable_strips cable_spacing = @heating_system.cable_spacing.to_f.round number_of_strips = item.calculate_number_of_fixing_strips_at_cable_spacing(cable_spacing) if number_of_strips&.> 0 tz_cable_accessories_qtys[ItemConstants::TZ_CABLE_GRIPSTRIP_SKU] ||= 0 tz_cable_accessories_qtys[ItemConstants::TZ_CABLE_GRIPSTRIP_SKU] += number_of_strips end end num_strips = ItemConstants::TZ_CABLE_STRIP_SKUS.map { |sku| tz_cable_accessories_qtys[sku].to_i }.sum if @heating_system.sub_floor_type&.seo_key&.index('concrete') && num_strips.positive? # we have concrete subfloor and TZ cable, we need concrete drill bits, screws and tape # figure out the number of screws num_screws = (num_strips * HeatingSystemItems::SCREWS_PER_STRIP_WITH_OVERAGE).round # figure out the length of tape length_tape = (num_strips * HeatingSystemItems::TAPE_FEET_PER_STRIP_WITH_OVERAGE).round qtys_by_sku = {} # calculate number of screw packs and drill bit of various skus # if we are most of the way into a 100 pack take that because of the included drill bit large_pack_size = HeatingSystemItems::CONCRETE_SCREW_PACK_LARGE_SIZE small_pack_size = HeatingSystemItems::CONCRETE_SCREW_PACK_SMALL_SIZE qtys_by_sku[ItemConstants::TZ_CABLE_CONCRETE_SCREW_LARGE_SKU] = (num_screws / large_pack_size).round # figure out if we need any small packs to reach the number of screws needed num_exc_screws = (num_screws - (large_pack_size * qtys_by_sku[ItemConstants::TZ_CABLE_CONCRETE_SCREW_LARGE_SKU])) qtys_by_sku[ItemConstants::TZ_CABLE_CONCRETE_SCREW_SMALL_SKU] = (num_exc_screws.to_f / small_pack_size).ceil if num_exc_screws.positive? # figure out if we need to enclose a drill bit qtys_by_sku[ItemConstants::TZ_CABLE_CONCRETE_DRILL_BIT_SKU] = 1 if qtys_by_sku[ItemConstants::TZ_CABLE_CONCRETE_SCREW_LARGE_SKU].zero? # calculate number of tape items of various skus # for now we assume only the TZ cable double-sided tape tape_roll_length = HeatingSystemItems::TZ_CABLE_TAPE_ROLL_LENGTH_FT qtys_by_sku[ItemConstants::TZ_CABLE_TAPE_SKU] = (length_tape.to_f / tape_roll_length).ceil # iterate over these to populate tz_cable_accessories_qtys, but only when qty > 0 qtys_by_sku.each do |sku, q| tz_cable_accessories_qtys[sku] = q if q.to_i.positive? end end [qty, tz_cable_accessories_qtys] end |
#calculate_tstat_qty_and_pm_control_qty(controls) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 104 def calculate_tstat_qty_and_pm_control_qty(controls) tstat_qty = 1 pm_control_qty = 0 # controls are an array of control hashes with a bundle sku, so group by that and take the first (nSpire series tstats use power modules in exactly the same way so they are all the same) which will yield an array with two values: a bundle sku and an array of control hashes, count the pms in the array of control hashes if controls.any? { |c| c['symbol'] == 'RELAY' } controls.each do |c| pm_control_qty += c['qty'] if c['symbol'] == 'RELAY' end end [tstat_qty, pm_control_qty] end |
#find_accessories(elements, controls) ⇒ Object
16 17 18 19 20 21 22 23 24 25 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 16 def find_accessories(elements, controls) @accessories = [] if @heating_system.heating_system_product_line.snow_melt? select_snow_melting_accessories(elements, controls) else select_floor_heating_accessories(elements, controls) select_underlayment(elements, controls) end self end |
#select_floor_heating_accessories(elements, controls) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 55 def select_floor_heating_accessories(elements, controls) # calculate number of rough-in kits with 2 conduits for the tstat and with 1 conduit for each pm tstat_qty, pm_control_qty = calculate_tstat_qty_and_pm_control_qty(controls) # calculate quantities of elements and tz cable accessories, like strips, tape and screws qty, tz_cable_accessories_qtys = calculate_elements_qty_and_tz_cable_accessories_qty(elements) # Batch load all accessory items upfront to avoid N+1 queries accessory_skus = @accessory_set.map { |a| a['sku'] }.compact items_by_sku = Item.where(sku: accessory_skus) .includes(:primary_product_line, :exclusive_item_group) .index_by(&:sku) # Determine if free-form cable accessories should be shown # Only show fixing strips, tape, concrete screws/drill bits when: # 1. No membrane is being used (membrane_recommended is false) # 2. AND subfloor is existing concrete show_freeform_cable_accessories = !@heating_system.membrane_recommended && @heating_system.sub_floor_type&.seo_key == 'existing-concrete-slab' # populate the accessories @accessory_set.each do |accessory| next unless (acc_item = items_by_sku[accessory['sku']]) # Skip free-form cable accessories when membrane is used or subfloor is not existing concrete next if FREEFORM_CABLE_ACCESSORY_SKUS.include?(accessory['sku']) && !show_freeform_cable_accessories if @heating_system.heating_system_product_line.is_cable_system? && tz_cable_accessories_qtys[accessory['sku']].to_i.positive? # @accessories << accessory.merge({'qty' => tz_cable_accessories_qtys[accessory['sku']], 'symbol' => 'CABLESTRIP'}) @accessories << accessory.merge({ 'qty' => tz_cable_accessories_qtys[accessory['sku']], 'symbol' => 'CABLESTRIP', 'price' => 0.0 }) # force TC stripts to be free with a quoted system elsif !acc_item.primary_product_line.is_cable_system? && !acc_item.is_membrane? # here we have non tz cable accessories @accessories << if acc_item.exclusive_item_group # installation kits, these need the exclusive groupings since you only choose one within the group ie radio button accessory.merge({ 'qty' => 1, 'exclusive_group_type' => acc_item.exclusive_item_group.key, 'exclusive_group_name' => acc_item.exclusive_item_group.name }) elsif acc_item.is_circuit_check? # circuit check: 1 per heating element accessory.merge({ 'qty' => qty }) else # any other accessory accessory.merge({ 'qty' => 1 }) end end end # treat tstat rough in kits separately since they are differently categorized @accessories << @rough_in_kit_for_tstat.merge({ 'qty' => tstat_qty }) if tstat_qty.positive? && @rough_in_kit_for_tstat # treat power module rough in kits separately since they are differently categorized return unless pm_control_qty.positive? @accessories << @rough_in_kit_for_power_module.merge({ 'qty' => pm_control_qty }) if @rough_in_kit_for_power_module end |
#select_snow_melting_accessories(elements, _controls) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 27 def select_snow_melting_accessories(elements, _controls) # add SMP sku if present accessory = @accessory_set.detect { |a| a['sku'] == 'SMP' } @accessories << accessory.merge({ 'qty' => 1 }) if accessory # add junction box sku based on the number of heating elements selected_junction_box = 0 selected_junction_box_num_cold_leads = HeatingSystemCalculator::HeatingSystemItems::JUNCTION_BOX_COLD_LEAD_MAX_NUMBERS[0] elements_count = elements.to_a.sum { |e| e['qty'] } HeatingSystemCalculator::HeatingSystemItems::JUNCTION_BOX_COLD_LEAD_MAX_NUMBERS.each_with_index do |jb_num_cold_leads, i| selected_junction_box = i selected_junction_box_num_cold_leads = HeatingSystemCalculator::HeatingSystemItems::JUNCTION_BOX_COLD_LEAD_MAX_NUMBERS[i] break if jb_num_cold_leads >= elements_count end accessory = @accessory_set.detect { |a| a['sku'] == ItemConstants::JUNCTION_BOX_SKUS[selected_junction_box] } qty = (elements_count.to_f / selected_junction_box_num_cold_leads).ceil @accessories << accessory.merge({ 'qty' => qty }) if accessory end |
#select_underlayment(_elements, _controls) ⇒ Object
173 174 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 |
# File 'app/services/heating_system_calculator/accessories_finder.rb', line 173 def select_underlayment(_elements, _controls) = HeatingElementProductLineOption.get_suggested_underlayments({ sqft: @heated_area.insulation_surface, sub_floor_type_id: @heating_system.sub_floor_type&.id, floor_type_id: @heating_system.floor_type.id, heating_system_type_name: @heating_system.heating_system_type_name, catalog_id: @heating_system.catalog.id, store_id: @heating_system.catalog.store_id, ideal_cable_spacing: @heating_system.cable_spacing, membrane_recommended: @heating_system.membrane_recommended }) .each do |sku, line| underlayment = @underlayment_set.detect { |u| u['sku'] == sku } if underlayment # Calculate area from width x length if not pre-computed (width/length in inches) unit_area = underlayment['area'] || (underlayment['width'].to_f * underlayment['length'].to_f / HeatingSystemItems::SQUARE_INCHES_PER_SQUARE_FOOT) total_sqft = (unit_area * line[:quantity]).round name_appended_blurb = total_sqft.positive? ? ", total sq.ft.: #{total_sqft}" : '' # Mark as recommended since it came from get_suggested_underlayments @accessories << underlayment.merge({ 'qty' => line[:quantity], 'name' => "#{underlayment['name']}#{name_appended_blurb}", 'recommended' => true }) else @error = { error_status: :underlayment_not_found, error_message: "Required underlayment with SKU #{sku} not found." } return self end end end |