Module: Models::MultiRoom

Extended by:
ActiveSupport::Concern
Included in:
Order, Quote
Defined in:
app/concerns/models/multi_room.rb

Overview

ActiveSupport::Concern mixin: multi room.

Has and belongs to many collapse

Instance Method Summary collapse

Instance Method Details

#add_line_items_to_all_rooms(sku, qty) ⇒ Object

Adds a line item with the given SKU and quantity to every room.

Parameters:

  • sku (String)

    the item SKU to add

  • qty (Integer)

    the quantity to add

Returns:

  • (Object)

    result of MultiRoomModel::RoomsLineItemChanger#add_line_items_to_all_rooms



242
243
244
# File 'app/concerns/models/multi_room.rb', line 242

def add_line_items_to_all_rooms(sku, qty)
  MultiRoomModel::RoomsLineItemChanger.new(self).add_line_items_to_all_rooms(sku, qty)
end

#add_lines_for_room_configuration(room_configuration) ⇒ Boolean

This callback is called whenever a room configuration is added, see :before_add in quote, order.
Synchronizes the room's line items onto this quote or order.

Parameters:

Returns:

  • (Boolean)

    false when the room is already attached (halting the add), true otherwise



110
111
112
113
114
115
116
117
# File 'app/concerns/models/multi_room.rb', line 110

def add_lines_for_room_configuration(room_configuration)
  return false if room_configurations.find { |rc| rc == room_configuration }

  logger.info "!!! add line call back called for #{room_configuration.id}"
  room_configuration.synchronize_lines(self)
  set_priority(false) if respond_to?(:set_priority)
  true
end

#all_rooms_complete?Boolean

Whether every room is complete or cancelled.

Returns:

  • (Boolean)


136
137
138
# File 'app/concerns/models/multi_room.rb', line 136

def all_rooms_complete?
  room_configurations.all? { |rc| rc.complete? || rc.cancelled? }
end

#all_rooms_complete_or_cancelled?Boolean

Whether every room is complete or cancelled.

Returns:

  • (Boolean)


157
158
159
# File 'app/concerns/models/multi_room.rb', line 157

def all_rooms_complete_or_cancelled?
  room_configurations.all? { |rc| rc.complete? || rc.cancelled? }
end

#all_rooms_complete_or_cancelled_or_draft?Boolean

Whether every room is complete, cancelled, or still in draft.

Returns:

  • (Boolean)


164
165
166
# File 'app/concerns/models/multi_room.rb', line 164

def all_rooms_complete_or_cancelled_or_draft?
  room_configurations.all? { |rc| rc.complete? || rc.cancelled? || rc.draft? }
end

#all_rooms_in_design?Boolean

Whether every room has reached the design stage or later.

Returns:

  • (Boolean)


122
123
124
# File 'app/concerns/models/multi_room.rb', line 122

def all_rooms_in_design?
  room_configurations.all?(&:in_design_or_later?)
end

#all_rooms_ppd?Boolean

Whether every room is still in the pre-production draft state.

Returns:

  • (Boolean)


150
151
152
# File 'app/concerns/models/multi_room.rb', line 150

def all_rooms_ppd?
  room_configurations.all?(&:draft?)
end

#any_room_ppd?Boolean

Whether any room is still in the pre-production draft state.

Returns:

  • (Boolean)


143
144
145
# File 'app/concerns/models/multi_room.rb', line 143

def any_room_ppd?
  room_configurations.any?(&:draft?)
end

#any_rooms_in_design?Boolean

Whether any room has reached the design stage or later.

Returns:

  • (Boolean)


129
130
131
# File 'app/concerns/models/multi_room.rb', line 129

def any_rooms_in_design?
  room_configurations.any?(&:in_design_or_later?)
end

#get_operating_costsHash

Aggregates operating cost estimates across all rooms.

Returns:

  • (Hash)

    { status: 'ok'/'error', message: String } plus, on success,
    :operating_costs_by_room, :operating_cost_annual,
    :operating_cost_by_month_average, and :operating_cost_by_coldest_month



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'app/concerns/models/multi_room.rb', line 180

def get_operating_costs
  return { status: 'error', message: 'there are no rooms to calculate operating costs' } unless room_configurations.any?

  res = { status: 'ok' }
  rc_ocs = room_configurations.map { |rc| rc.get_operating_costs.merge({ room_configuration_id: rc.id, room_name: rc.name_with_room }) }
  res[:operating_costs_by_room] = rc_ocs
  if rc_ocs.all? { |oc| oc[:status] == 'ok' }
    res[:operating_cost_annual] = rc_ocs.sum { |oc| oc[:operating_cost_annual] }
    res[:operating_cost_by_month_average] = rc_ocs.sum { |oc| oc[:operating_cost_by_month_average] }
    res[:operating_cost_by_coldest_month] = rc_ocs.sum { |oc| oc[:operating_cost_by_coldest_month] }
    res[:message] = 'assuming default thermostat settings, based on average monthly temperatures and electricity rates in your area.'
  else
    res[:status] = 'error'
    res[:message] = rc_ocs.reject { |oc| oc[:status] == 'ok' }.map { |oc| "Room: #{oc[:room_name]}: #{oc[:message]}" }.join(', ')
  end
  res
end

Aggregates recommended accessory materials across all rooms (or one room),
merging duplicate SKUs by summing their quantities.

Parameters:

  • options (Hash) (defaults to: {})

    options forwarded to each room's own
    get_recommended_materials, plus the room filter below

Options Hash (options):

  • for_room (RoomConfiguration, nil)

    limit recommendations to this
    single room instead of all rooms with line items

Returns:

  • (Array<Hash>)

    recommended accessories, each with at least 'sku' and 'qty' keys



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'app/concerns/models/multi_room.rb', line 206

def get_recommended_materials(options = {})
  recommended_accessories = []
  if options[:for_room]
    rooms = [options[:for_room]]
  else
    room_ids = line_items.pluck(Arel.sql('distinct room_configuration_id')).compact
    rooms = RoomConfiguration.where(id: room_ids)
  end
  logger.debug "get_recommended_materials(options=#{options.inspect}), rooms.map{|r| r.id}: #{rooms.map(&:id).inspect}"
  rooms.each do |rc|
    rc.get_recommended_materials(options).each do |rc_acc|
      logger.debug "get_recommended_materials, rc: #{rc.id}, rc_acc: #{rc_acc.inspect}"
      found = false
      recommended_accessories.each_with_index do |a, i|
        next unless a['sku'] == rc_acc['sku']

        logger.debug "get_recommended_materials, found: was: #{recommended_accessories[i]['qty']}"
        recommended_accessories[i]['qty'] += rc_acc['qty']
        found = true
        logger.debug "get_recommended_materials, found: now: #{recommended_accessories[i]['qty']}"
      end
      unless found
        logger.debug "get_recommended_materials, not found: adding: #{rc_acc.inspect}"
        recommended_accessories << rc_acc
      end
    end
  end
  logger.debug "get_recommended_materials: recommended_accessories: #{recommended_accessories.inspect}"
  recommended_accessories
end

#heated_sq_ftInteger

Total heated floor area across all rooms that have a measured square footage.

Returns:

  • (Integer)

    sum of each room's installation square footage



31
32
33
34
35
36
37
# File 'app/concerns/models/multi_room.rb', line 31

def heated_sq_ft
  sqft = 0
  room_configurations.each do |rc|
    sqft += rc.installation_sqft.to_i if rc.square_footage.to_i > 0
  end
  sqft
end

#insulation_sq_ftInteger

Total insulation surface area across indoor rooms.

Returns:

  • (Integer)

    sum of each indoor room's insulation surface



42
43
44
45
46
47
48
# File 'app/concerns/models/multi_room.rb', line 42

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

#prioritize_room(room_configuration) ⇒ void

This method returns an undefined value.

Marks a room configuration for same-day handling when added to an order.

Parameters:



21
22
23
24
25
26
# File 'app/concerns/models/multi_room.rb', line 21

def prioritize_room(room_configuration)
  return unless is_a? Order
  return if room_configuration.draft? || room_configuration.complete?

  room_configuration.update_attribute!(:priority, 'same day order')
end

#remove_line_items_from_all_rooms(sku) ⇒ Object

Removes the line item with the given SKU from every room.

Parameters:

  • sku (String)

    the item SKU to remove

Returns:

  • (Object)

    result of MultiRoomModel::RoomsLineItemChanger#remove_line_items_from_all_rooms



250
251
252
# File 'app/concerns/models/multi_room.rb', line 250

def remove_line_items_from_all_rooms(sku)
  MultiRoomModel::RoomsLineItemChanger.new(self).remove_line_items_from_all_rooms(sku)
end

#remove_lines_for_room_configuration(room_configuration) ⇒ Boolean

This callback is called whenever a room configuration is removed, see :before_remove in quote, order
It removes associated line items if editing is allowed, or blocks removal if line items can't be removed.

Parameters:

Returns:

  • (Boolean)

    true when removal may proceed

Raises:

  • (ActiveRecord::RecordNotDestroyed)

    when editing is locked or line items could not be removed



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
103
# File 'app/concerns/models/multi_room.rb', line 75

def remove_lines_for_room_configuration(room_configuration)
  logger.info "MultiRoom#remove_lines_for_room_configuration called for room #{room_configuration.id} on #{self.class.name} #{id}"

  # Find line items associated with this room (query DB to avoid stale cache)
  room_line_items = line_items.where(room_configuration_id: room_configuration.id)

  if room_line_items.exists?
    # Check if editing is locked on this resource
    if respond_to?(:editing_locked?) && editing_locked?
      logger.error "Cannot remove room #{room_configuration.id} from #{self.class.name} #{id}: editing is locked and room has #{room_line_items.count} line items"
      raise ActiveRecord::RecordNotDestroyed, "Cannot remove room '#{room_configuration.name}' because this #{self.class.name.underscore.humanize.downcase} is locked for editing and has line items associated with that room"
    end

    # Remove associated line items
    logger.info "Removing #{room_line_items.count} line items for room #{room_configuration.id}"
    room_line_items.destroy_all

    # Verify all line items were removed
    remaining = line_items.reload.where(room_configuration_id: room_configuration.id)
    if remaining.exists?
      logger.error "Failed to remove all line items for room #{room_configuration.id}: #{remaining.count} remain"
      raise ActiveRecord::RecordNotDestroyed, "Cannot remove room '#{room_configuration.name}': #{remaining.count} line items could not be removed"
    end
  end

  set_priority(false) if respond_to?(:set_priority)
  self.recalculate_shipping = true
  true
end

#replace_line_items_in_all_rooms(orig_sku, new_sku) ⇒ Object

Replaces a line item SKU with another SKU in every room.

Parameters:

  • orig_sku (String)

    the item SKU to replace

  • new_sku (String)

    the item SKU to replace it with

Returns:

  • (Object)

    result of MultiRoomModel::RoomsLineItemChanger#replace_line_items_in_all_rooms



259
260
261
# File 'app/concerns/models/multi_room.rb', line 259

def replace_line_items_in_all_rooms(orig_sku, new_sku)
  MultiRoomModel::RoomsLineItemChanger.new(self).replace_line_items_in_all_rooms(orig_sku, new_sku)
end

#room_configurationsActiveRecord::Relation<RoomConfiguration>

Room configurations attached to this quote or order.

Returns:

See Also:



11
12
13
14
# File 'app/concerns/models/multi_room.rb', line 11

has_and_belongs_to_many :room_configurations,
before_remove: :remove_lines_for_room_configuration,
before_add: :add_lines_for_room_configuration,
after_add: :prioritize_room

#suggested_itemsHash

Suggested line items for this quote or order, aggregated across rooms.

Returns:

  • (Hash)

    suggested items keyed by SKU or item identifier



53
54
55
56
57
58
59
60
# File 'app/concerns/models/multi_room.rb', line 53

def suggested_items
  # If we have tempzone, gather all sq.ft
  suggested_items_list = {}
  # room_configurations.sort_by(&:name).each{|rc| rc.append_suggested_items(suggested_items_list, self) }
  # self.append_suggested_towel_warmers(suggested_items_list)
  append_suggested_items(suggested_items_list, self)
  suggested_items_list
end

#suggested_servicesArray

Suggested services for this quote or order. Currently unused; always empty.

Returns:

  • (Array)

    empty array



65
66
67
# File 'app/concerns/models/multi_room.rb', line 65

def suggested_services
  []
end

#synchronization_targetsActiveRecord::Relation<RoomConfiguration>

A quote or order always sync to all its rooms since non item locked might have changed

Returns:



171
172
173
# File 'app/concerns/models/multi_room.rb', line 171

def synchronization_targets
  room_configurations
end