Class: Item::KitConsolidator

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

Overview

Service object: kit consolidator.

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

Parameters:

  • item (Item)

    the kit item to consolidate

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

    Options hash

Options Hash (options):

  • :store_id (Integer)

    restrict store items to this store

  • :logger (Logger)

    custom logger (defaults to Rails.logger)



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'app/services/item/kit_consolidator.rb', line 15

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.



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

def item
  @item
end

#item_changesObject (readonly)

Returns the value of attribute item_changes.



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

def item_changes
  @item_changes
end

#loggerObject (readonly)

Returns the value of attribute logger.



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

def logger
  @logger
end

#store_item_changesObject (readonly)

Returns the value of attribute store_item_changes.



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

def store_item_changes
  @store_item_changes
end

Class Method Details

.consolidate_all_kitsObject

this will call all consolidate methods on all kit items



32
33
34
# File 'app/services/item/kit_consolidator.rb', line 32

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



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'app/services/item/kit_consolidator.rb', line 105

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



37
38
39
40
41
42
43
44
45
# File 'app/services/item/kit_consolidator.rb', line 37

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

#consolidate_committed_qtyObject



68
69
70
71
72
73
74
75
76
77
# File 'app/services/item/kit_consolidator.rb', line 68

def consolidate_committed_qty
  return self 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



57
58
59
60
61
62
63
64
65
66
# File 'app/services/item/kit_consolidator.rb', line 57

def consolidate_qty
  return self 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



80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'app/services/item/kit_consolidator.rb', line 80

def consolidate_unit_cogs
  return self 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



96
97
98
99
100
101
102
# File 'app/services/item/kit_consolidator.rb', line 96

def consolidate_weights_and_dimensions
  return self 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



47
48
49
50
51
52
53
54
# File 'app/services/item/kit_consolidator.rb', line 47

def populate_components
  return self if @components.empty?

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