Class: Item::KitConsolidator

Inherits:
Object
  • Object
show all
Defined in:
app/services/item/kit_consolidator.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(item, options = {}) ⇒ KitConsolidator

initialize the kit builder, set store items and kit components (with their associated target item and store items) in variables so we only have to find them once and can refer to them in the consolidate methods
initialize the @store_item_changes and @item_changes where we will store any changes we want to submit
raise exception if we detect it's not a kit
if it has no components everything will be set to 0



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'app/services/item/kit_consolidator.rb', line 10

def initialize(item, options = {})
  raise "Invalid item type, item #{item.id} must be a kit" unless item.is_kit?

  @item = item
  @store_items = @item.store_items.available
  # only recalculate changes to store items from a specific store, if we are passed that information
  @store_items = @store_items.where(store_id: options[:store_id]) if options[:store_id]
  @options = options
  @components = build_components
  @logger = options[:logger] || Rails.logger
  @logger.error "Item #{@item.id} has no components" if @components.empty?
  @store_item_changes = Hash.new { |h, k| h[k] = {} }
  @item_changes = {}
  @consolidated_child_kits = { qty: Set.new, committed_qty: Set.new, unit_cogs: Set.new }
end

Instance Attribute Details

#itemObject (readonly)

Returns the value of attribute item.



4
5
6
# File 'app/services/item/kit_consolidator.rb', line 4

def item
  @item
end

#item_changesObject (readonly)

Returns the value of attribute item_changes.



4
5
6
# File 'app/services/item/kit_consolidator.rb', line 4

def item_changes
  @item_changes
end

#loggerObject (readonly)

Returns the value of attribute logger.



4
5
6
# File 'app/services/item/kit_consolidator.rb', line 4

def logger
  @logger
end

#store_item_changesObject (readonly)

Returns the value of attribute store_item_changes.



4
5
6
# File 'app/services/item/kit_consolidator.rb', line 4

def store_item_changes
  @store_item_changes
end

Class Method Details

.consolidate_all_kitsObject

this will call all consolidate methods on all kit items



27
28
29
# File 'app/services/item/kit_consolidator.rb', line 27

def self.consolidate_all_kits
  Item.kits.each { |item| new(item).consolidate_all_fields.commit }
end

Instance Method Details

#commitObject

send the db updates to items and store_items tables based on new values we have stored in @store_item_changes and @item_changes, wrap it in a transaction in case part of it fails then it will roll back



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'app/services/item/kit_consolidator.rb', line 100

def commit
  return false if @components.empty?
  Item.transaction do
    PaperTrail.request(whodunnit: 'Item::KitConsolidator') do
      logger.tagged("Item(#{@item.id})::KitBuilder") do
        logger.debug("Item changes", change_count: @item_changes&.size)
        @item.update!(@item_changes) if @item_changes.any?
        store_items_by_id = StoreItem.where(id: @store_item_changes.keys)
                                     .includes(:item, :serial_numbers)
                                     .index_by(&:id)
        @store_item_changes.each do |id, attrs|
          logger.debug("Updating store_item", store_item_id: id)
          store_item = store_items_by_id[id]
          raise ActiveRecord::RecordNotFound, "Couldn't find StoreItem with 'id'=#{id}" if store_item.nil?

          store_item.skip_check_kit_components = true
          store_item.update!(attrs)
        end
      end
    end
  end
  true
end

#consolidate_all_fieldsObject

run all consolidate methods on an item



32
33
34
35
36
37
38
39
40
# File 'app/services/item/kit_consolidator.rb', line 32

def consolidate_all_fields
  if @components.present?
    populate_components
    consolidate_qty
    consolidate_unit_cogs
    consolidate_weights_and_dimensions
  end
  self
end

#consolidate_committed_qtyObject



63
64
65
66
67
68
69
70
71
72
# File 'app/services/item/kit_consolidator.rb', line 63

def consolidate_committed_qty
  return nil if @components.empty?

  consolidate_child_kits_once!(:committed_qty, :consolidate_committed_qty)
  @store_items.each do |si|
    on_hand, available = calculate_quantities(si.store_id)
    add_store_item_changes(si.id, qty_committed: on_hand - available)
  end
  self
end

#consolidate_qtyObject

determine qty_on_hand and qty_committed of store items based on qty_on_hand and qty_available of component items



52
53
54
55
56
57
58
59
60
61
# File 'app/services/item/kit_consolidator.rb', line 52

def consolidate_qty
  return nil if @components.empty?

  consolidate_child_kits_once!(:qty, :consolidate_qty)
  @store_items.each do |si|
    on_hand, available = calculate_quantities(si.store_id)
    add_store_item_changes(si.id, qty_on_hand: on_hand, qty_committed: on_hand - available)
  end
  self
end

#consolidate_unit_cogsObject

calculate unit_cogs of store items by adding up unit_cogs of all component items



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'app/services/item/kit_consolidator.rb', line 75

def consolidate_unit_cogs
  return nil if @components.empty?

  consolidate_child_kits_once!(:unit_cogs, :consolidate_unit_cogs)
  @store_items.each do |si|
    unit_cogs = BigDecimal(0)
    @components.each do |c|
      unit_cogs += (c[:store_items][si.store_id].try(:unit_cogs) || 0) * c[:item_relation].quantity
    end
    add_store_item_changes(si.id, unit_cogs:)
  end
  self
end

#consolidate_weights_and_dimensionsObject

calculate base_weight and shipping_weight of item by adding up component item weights
determine shipping width, length and height to use by comparing volume of component item dimensions and using the one with the largest volume



91
92
93
94
95
96
97
# File 'app/services/item/kit_consolidator.rb', line 91

def consolidate_weights_and_dimensions
  return nil if @components.empty?

  %i[gross_weight base_weight shipping_weight].each { |weight_attr| consolidate_weight(weight_attr) } unless item.unique_kit_dimensions
  consolidate_shipping_dimensions unless item.unique_kit_dimensions
  self
end

#populate_componentsObject



42
43
44
45
46
47
48
49
# File 'app/services/item/kit_consolidator.rb', line 42

def populate_components
  return nil if @components.empty?

  @item.catalog_items.each do |ci|
    ci.create_component_catalog_items if ci.missing_kit_components?
  end
  self
end