Class: StoreItem

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable
Defined in:
app/models/store_item.rb

Overview

== Schema Information

Table name: store_items
Database name: primary

id :integer not null, primary key
add_to_next_cycle_count :boolean
always_add_to_cycle_count :string
availability_end_date :date
availability_start_date :date
business_days_to_fulfill :integer default(0)
cycle_count_priority :integer default(0)
deficit :integer default(0)
handling_charge :decimal(, ) default(0.0), not null
is_discontinued :boolean default(FALSE)
last_inventory_updated_at :datetime
last_reported_stock :jsonb
location :string default("AVAILABLE"), not null
must_be_shipped_insured :boolean
permanent_qty_available :integer
qty_available :integer default(0)
qty_committed :integer default(0)
qty_on_hand :integer default(0)
quantity_limit :integer
requires_serial_number :boolean default(FALSE)
safety_stock_days :integer
safety_stock_fixed :integer
safety_stock_percentage :decimal(, )
unit_cogs :decimal(, ) default(0.0), not null
created_at :datetime
updated_at :datetime
item_id :integer not null
last_item_ledger_entry_id :integer
store_id :integer

Indexes

idx_item_id_qty_on_hand (item_id,qty_on_hand)
idx_litem_id_is_discontinued (item_id,is_discontinued)
idx_location_store_id (location,store_id)
idx_store_items_unique (item_id,store_id,location) UNIQUE
index_store_items_on_item_id_and_id (item_id,id)
index_store_items_on_last_inventory_updated_at (last_inventory_updated_at) USING brin
index_store_items_store_id (store_id)

Foreign Keys

store_items_item_id_fk (item_id => items.id) ON DELETE => cascade

Constant Summary collapse

BO_JOIN_SQL =
"(select sum(line_items.quantity) from line_items inner join orders on orders.id = line_items.resource_id and line_items.resource_type = 'Order' and orders.state = 'crm_back_order'  inner join catalog_items on catalog_items.id = line_items.catalog_item_id where catalog_items.store_item_id = store_items.id)"
POSSIBLE_LOCATIONS =
%w[AVAILABLE SCRAP OBSOLETE HELD REFURB TECH RESTOCK CLAIM CLAIM_SUBMITTED SERVICEVAN MARKETING].freeze
POSSIBLE_LOCATIONS_FOR_RMAS =
%w[AVAILABLE SCRAP REFURB CLAIM RESTOCK].freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has and belongs to many collapse

Has many collapse

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransortable_attributes, #to_relation

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#handling_chargeObject (readonly)



100
# File 'app/models/store_item.rb', line 100

validates :store_id, :qty_on_hand, :qty_committed, :handling_charge, :location, :unit_cogs, presence: true

#locationObject (readonly)



100
# File 'app/models/store_item.rb', line 100

validates :store_id, :qty_on_hand, :qty_committed, :handling_charge, :location, :unit_cogs, presence: true

#qty_committedObject (readonly)



100
# File 'app/models/store_item.rb', line 100

validates :store_id, :qty_on_hand, :qty_committed, :handling_charge, :location, :unit_cogs, presence: true

#qty_on_handObject (readonly)



100
# File 'app/models/store_item.rb', line 100

validates :store_id, :qty_on_hand, :qty_committed, :handling_charge, :location, :unit_cogs, presence: true

#skip_check_kit_componentsObject

Returns the value of attribute skip_check_kit_components.



55
56
57
# File 'app/models/store_item.rb', line 55

def skip_check_kit_components
  @skip_check_kit_components
end

#store_idObject (readonly)



100
# File 'app/models/store_item.rb', line 100

validates :store_id, :qty_on_hand, :qty_committed, :handling_charge, :location, :unit_cogs, presence: true

#unit_cogsObject (readonly)



100
# File 'app/models/store_item.rb', line 100

validates :store_id, :qty_on_hand, :qty_committed, :handling_charge, :location, :unit_cogs, presence: true

Class Method Details

.activeActiveRecord::Relation<StoreItem>

A relation of StoreItems that are active. Active Record Scope

Returns:

See Also:



132
# File 'app/models/store_item.rb', line 132

scope :active, -> { includes(:item).where(is_discontinued: false) }

.availableActiveRecord::Relation<StoreItem>

A relation of StoreItems that are available. Active Record Scope

Returns:

See Also:



133
# File 'app/models/store_item.rb', line 133

scope :available, -> { where(location: 'AVAILABLE') }

.available_firstActiveRecord::Relation<StoreItem>

A relation of StoreItems that are available first. Active Record Scope

Returns:

See Also:



131
# File 'app/models/store_item.rb', line 131

scope :available_first, -> { order(Arel.sql("location <> 'AVAILABLE', store_id")) }

.backorders_onlyActiveRecord::Relation<StoreItem>

A relation of StoreItems that are backorders only. Active Record Scope

Returns:

See Also:



135
# File 'app/models/store_item.rb', line 135

scope :backorders_only, -> { where("#{BO_JOIN_SQL} > 0") }

.by_amazon_asinsActiveRecord::Relation<StoreItem>

A relation of StoreItems that are by amazon asins. Active Record Scope

Returns:

See Also:



149
# File 'app/models/store_item.rb', line 149

scope :by_amazon_asins, ->(*asins) { available.includes(:item).where(items: { amazon_asin: [asins].flatten.uniq.compact }) }

.by_skusActiveRecord::Relation<StoreItem>

A relation of StoreItems that are by skus. Active Record Scope

Returns:

See Also:



147
# File 'app/models/store_item.rb', line 147

scope :by_skus, ->(*skus) { available.includes(:item).where(items: { sku: [skus].flatten.uniq.compact }) }

.by_upcsActiveRecord::Relation<StoreItem>

A relation of StoreItems that are by upcs. Active Record Scope

Returns:

See Also:



148
# File 'app/models/store_item.rb', line 148

scope :by_upcs, ->(*upcs) { available.includes(:item).where(items: { upc: [upcs].flatten.uniq.compact }) }

.category_sortedActiveRecord::Relation<StoreItem>

A relation of StoreItems that are category sorted. Active Record Scope

Returns:

See Also:



136
# File 'app/models/store_item.rb', line 136

scope :category_sorted, -> { joins(item: :product_category).order('product_categories.name, items.sku, store_items.store_id') }

.create_fake_fallback_packagings(logger = Rails.logger) ⇒ Object



489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'app/models/store_item.rb', line 489

def self.create_fake_fallback_packagings(logger = Rails.logger)
  affected_store_items = StoreItem.packaging_smaller_than_shipping_dimensions(tolerance = 0.1) # fraction so 0.1 = within 10%
  store_item_count = affected_store_items.length

  logger.info "$$$ Affected Store Items to process: #{store_item_count}."
  store_item_counter = 0
  affected_store_items.each do |store_item|
    store_item.packagings.find_all { |p| !p.warehouse_package.can_contain_dimensions?(store_item.shipping_dimensions) }.each do |p|
      logger.debug('Removing packaging', packaging_id: p.id)
      p.destroy
    end
    if store_item.packagings.empty?
      warehouse_package = WarehousePackage.find_or_create_closest_package_within_tolerance(store_item.store_id, store_item.shipping_dimensions, tolerance = 0.0, "FAKE STOPGAP PKG FOR #{store_item.name}", true)
      logger.debug('Found or creating warehouse_package', warehouse_package_id: warehouse_package&.id)
      packaging = Packaging.new(number_items: 1, warehouse_package_id: warehouse_package.id, store_item_id: store_item.id)
      store_item.packagings << packaging
      logger.debug('Creating packaging', packaging_id: packaging&.id)
    end
    logger.info "Processed affected store_item: ID: #{store_item.id}: #{store_item.name}"
    store_item_counter += 1
    logger.info "*** Successfully processed #{store_item_counter} affected shipments out of #{store_item_count},  #{Time.current}"
  end
  true
end

.for_product_line_id_exactActiveRecord::Relation<StoreItem>

A relation of StoreItems that are for product line id exact. Active Record Scope

Returns:

See Also:



142
# File 'app/models/store_item.rb', line 142

scope :for_product_line_id_exact, ->(pid) { where('EXISTS(select 1 from item_product_lines ipl where ipl.item_id = items.id and ipl.product_line_id IN (?))', pid) }

.for_product_line_id_with_complimentariesActiveRecord::Relation<StoreItem>

A relation of StoreItems that are for product line id with complimentaries. Active Record Scope

Returns:

See Also:



141
# File 'app/models/store_item.rb', line 141

scope :for_product_line_id_with_complimentaries, ->(pid) { for_product_line_id_exact(ProductLine.find(pid).full_complimentary_product_line_ids) }

.in_storage_locationsActiveRecord::Relation<StoreItem>

A relation of StoreItems that are in storage locations. Active Record Scope

Returns:

See Also:



145
# File 'app/models/store_item.rb', line 145

scope :in_storage_locations, ->(*slids) { where('EXISTS(select 1 from storage_locations_store_items slsi WHERE slsi.store_item_id = store_items.id AND slsi.storage_location_id IN (?))', [slids].flatten.map(&:presence).uniq.compact.map(&:to_i)) }

.item_by_product_category_idActiveRecord::Relation<StoreItem>

A relation of StoreItems that are item by product category id. Active Record Scope

Returns:

See Also:



139
# File 'app/models/store_item.rb', line 139

scope :item_by_product_category_id, ->(product_category_ids) { joins(:item).merge(Item.by_product_category_id(product_category_ids)) }

.item_by_product_line_idActiveRecord::Relation<StoreItem>

A relation of StoreItems that are item by product line id. Active Record Scope

Returns:

See Also:



140
# File 'app/models/store_item.rb', line 140

scope :item_by_product_line_id, ->(*product_line_ids) { joins(:item).merge(Item.by_product_line_id(product_line_ids)) }

.item_keywords_searchActiveRecord::Relation<StoreItem>

A relation of StoreItems that are item keywords search. Active Record Scope

Returns:

See Also:



138
# File 'app/models/store_item.rb', line 138

scope :item_keywords_search, ->(keywords) { joins(:item).merge(Item.keywords_search(keywords)) }

.locations_for_selectObject



163
164
165
# File 'app/models/store_item.rb', line 163

def self.locations_for_select
  POSSIBLE_LOCATIONS.map { |l| [l.humanize, l] }
end

.must_be_shipped_insuredActiveRecord::Relation<StoreItem>

A relation of StoreItems that are must be shipped insured. Active Record Scope

Returns:

See Also:



143
# File 'app/models/store_item.rb', line 143

scope :must_be_shipped_insured, -> { where(must_be_shipped_insured: true) }

.not_in_catalog_idActiveRecord::Relation<StoreItem>

A relation of StoreItems that are not in catalog id. Active Record Scope

Returns:

See Also:



137
# File 'app/models/store_item.rb', line 137

scope :not_in_catalog_id, ->(catalog_id) { where.not('exists(select 1 from catalog_items ci where ci.catalog_id = ? and ci.store_item_id = store_items.id)', catalog_id) }

.packaging_smaller_than_shipping_dimensions(tolerance = 0.0) ⇒ Object

fraction so 0.1 = within 10%



354
355
356
357
358
359
360
# File 'app/models/store_item.rb', line 354

def self.packaging_smaller_than_shipping_dimensions(tolerance = 0.0) # fraction so 0.1 = within 10%
  StoreItem.active.find_all do |store_item|
    store_item.shipping_dimensions && (store_item.packagings.empty? || store_item.packagings.any? do |p|
      p.warehouse_package.nil? || !p.warehouse_package.can_contain_dimensions?(store_item.shipping_dimensions, tolerance)
    end)
  end
end

.ransackable_scopes(_auth_object = nil) ⇒ Object



159
160
161
# File 'app/models/store_item.rb', line 159

def self.ransackable_scopes(_auth_object = nil)
  %i[for_product_line_id_with_complimentaries item_keywords_search item_by_product_line_id item_by_product_category_id in_storage_locations]
end

.stock_items_without_locationActiveRecord::Relation<StoreItem>

A relation of StoreItems that are stock items without location. Active Record Scope

Returns:

See Also:



144
# File 'app/models/store_item.rb', line 144

scope :stock_items_without_location, -> { where(StoreItem[:qty_on_hand].gteq(1)).where(is_discontinued: false).where('NOT exists(select 1 from storage_locations_store_items slsi where slsi.store_item_id = store_items.id)') }

.warmlyyours_warehousesActiveRecord::Relation<StoreItem>

A relation of StoreItems that are warmlyyours warehouses. Active Record Scope

Returns:

See Also:



146
# File 'app/models/store_item.rb', line 146

scope :warmlyyours_warehouses, -> { joins(:store).merge(Store.warmlyyours_warehouses) }

.with_back_order_quantitiesActiveRecord::Relation<StoreItem>

A relation of StoreItems that are with back order quantities. Active Record Scope

Returns:

See Also:



134
# File 'app/models/store_item.rb', line 134

scope :with_back_order_quantities, -> { select("#{BO_JOIN_SQL} as qty_bo, *") }

Instance Method Details

#associated_catalog_itemsActiveRecord::Relation<CatalogItem>

Returns:

See Also:



78
# File 'app/models/store_item.rb', line 78

has_and_belongs_to_many :associated_catalog_items, class_name: 'CatalogItem', inverse_of: :store_items

#available_location?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'app/models/store_item.rb', line 187

def available_location?
  location == 'AVAILABLE'
end

#catalog_itemsActiveRecord::Relation<CatalogItem>

Returns:

See Also:



81
# File 'app/models/store_item.rb', line 81

has_many :catalog_items, dependent: :destroy, inverse_of: :store_item

#check_ok_to_destroyObject



348
349
350
351
352
# File 'app/models/store_item.rb', line 348

def check_ok_to_destroy
  errors.add(:base, 'Cannot destroy because there are dependent catalog and/or line_items, please discontinue instead') if usage > 0
  errors.add(:base, "Cannot destroy because #{sku} is linked to one or more active kits, please discontinue instead") if is_part_of_an_active_kit?
  errors.none?
end

#deep_dupObject



59
60
61
62
63
64
65
66
67
68
69
70
# File 'app/models/store_item.rb', line 59

def deep_dup
  deep_clone(
    include: [:packagings, :catalog_items],
    except: :last_item_ledger_entry_id
  ) do |_original, copy|
    if copy.is_a?(StoreItem)
      copy.qty_on_hand = 0
      copy.qty_committed = 0
      copy.unit_cogs = 0
    end
  end
end

#get_kit_store_itemsObject



370
371
372
# File 'app/models/store_item.rb', line 370

def get_kit_store_items
  store.store_items.available.where(item_id: item.get_kit_item_ids)
end

#get_self_and_kit_store_itemsObject



374
375
376
# File 'app/models/store_item.rb', line 374

def get_self_and_kit_store_items
  store.store_items.available.where(item_id: item.get_self_and_kit_item_ids)
end

#has_non_discontinued_catalog_items?Boolean

Returns:

  • (Boolean)


392
393
394
# File 'app/models/store_item.rb', line 392

def has_non_discontinued_catalog_items?
  catalog_items.where.not(state: %w[pending_discontinue discontinued]).exists?
end

#inventory_commitsActiveRecord::Relation<InventoryCommit>

Returns:

See Also:



86
# File 'app/models/store_item.rb', line 86

has_many :inventory_commits

#is_available_to_publicObject

Alias for Item#is_available_to_public

Returns:

  • (Object)

    Item#is_available_to_public

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_cable_accessory?Object

Alias for Item#is_cable_accessory?

Returns:

  • (Object)

    Item#is_cable_accessory?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_cable_fit_guide?Object

Alias for Item#is_cable_fit_guide?

Returns:

  • (Object)

    Item#is_cable_fit_guide?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_circuit_check?Object

Alias for Item#is_circuit_check?

Returns:

  • (Object)

    Item#is_circuit_check?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_control?Object

Alias for Item#is_control?

Returns:

  • (Object)

    Item#is_control?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_heating_element?Object

Alias for Item#is_heating_element?

Returns:

  • (Object)

    Item#is_heating_element?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_kit?Object

Alias for Item#is_kit?

Returns:

  • (Object)

    Item#is_kit?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_membrane?Object

Alias for Item#is_membrane?

Returns:

  • (Object)

    Item#is_membrane?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_publication?Object

Alias for Item#is_publication?

Returns:

  • (Object)

    Item#is_publication?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_roughin_kit?Object

Alias for Item#is_roughin_kit?

Returns:

  • (Object)

    Item#is_roughin_kit?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_service?Object

Alias for Item#is_service?

Returns:

  • (Object)

    Item#is_service?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_snow_melt_plaque?Object

Alias for Item#is_snow_melt_plaque?

Returns:

  • (Object)

    Item#is_snow_melt_plaque?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#is_underlayment?Object

Alias for Item#is_underlayment?

Returns:

  • (Object)

    Item#is_underlayment?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#itemItem

Returns:

See Also:



75
# File 'app/models/store_item.rb', line 75

belongs_to :item, inverse_of: :store_items, optional: true

#kit_component_inventory_commitsActiveRecord::Relation<InventoryCommit>

Returns:

See Also:



87
# File 'app/models/store_item.rb', line 87

has_many :kit_component_inventory_commits, class_name: 'InventoryCommit', foreign_key: 'kit_store_item_id'

#last_cycle_countObject



191
192
193
# File 'app/models/store_item.rb', line 191

def last_cycle_count
  CycleCountItem.joins(:cycle_count).where(cycle_counts: { store_id: store_id }, item_id: item_id, location: location, state: 'processed').order('updated_at DESC').first
end

#last_item_ledger_entryItemLedgerEntry



76
# File 'app/models/store_item.rb', line 76

belongs_to :last_item_ledger_entry, class_name: 'ItemLedgerEntry', optional: true

#line_itemsActiveRecord::Relation<LineItem>

Returns:

See Also:



82
# File 'app/models/store_item.rb', line 82

has_many :line_items, through: :catalog_items

#move_location(new_location, quantity: nil, gl_date: nil, serial_number_id: nil) ⇒ Object



378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'app/models/store_item.rb', line 378

def move_location(new_location, quantity: nil, gl_date: nil, serial_number_id: nil)
  gl_date ||= Date.current
  quantity ||= qty_on_hand
  line_items = {
    0 => {
      store_item_id: id,
      qty: quantity,
      serial_number_id: serial_number_id,
      new_location: new_location
    }
  }
  ItemLedgerEntry.location_transfer(store, line_items, gl_date, nil)
end

#nameObject



366
367
368
# File 'app/models/store_item.rb', line 366

def name
  "#{item.sku} / Store Item #{id}"
end

#next_availableObject



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'app/models/store_item.rb', line 265

def next_available
  logger.tagged("Item #{item.sku} -> next_available") do
    if item.is_kit?
      logger.debug 'Kit detected'
      # Get all order data
      buildable_qty = []
      buildable_date = []
      # We only analyze items with no availability
      get_kit_store_items.select { |si| si.qty_available <= 0 }.each do |ksi|
        logger.debug "Looking at next availability for #{ksi.item.sku}, store_item_id: #{ksi.id}"
        # Focus only on those components with no quantities
        if ksi_next_available = ksi.next_available
          buildable_qty << ksi_next_available.next_available_qty
          buildable_date << ksi_next_available.next_available_date
        end
        logger.debug "Next availability returned #{ksi_next_available.inspect}"
      end
      # Our most conservative approach will be the smallest quantity buildable at the latest date
      next_avail_qty = buildable_qty.min
      next_avail_date = buildable_date.compact.max
      return nil unless next_avail_qty && next_avail_date

      return OpenStruct.new(next_available_qty: buildable_qty.min,
                            next_available_date: buildable_date.compact.max).freeze

    elsif earliest_order_data = safe_on_order_first
      logger.debug 'Non kit with quantities on order'
      return OpenStruct.new(next_available_qty: earliest_order_data[:total_open],
                            next_available_date: earliest_order_data[:promised_delivery_date]).freeze
    else
      logger.debug 'Non kit, nothing on order'
      return nil
    end
  end
end

#next_available_with_depth_limit(max_depth: 10, current_depth: 0) ⇒ Object

Depth-limited version to prevent infinite recursion



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'app/models/store_item.rb', line 302

def next_available_with_depth_limit(max_depth: 10, current_depth: 0)
  # Prevent infinite recursion
  if current_depth >= max_depth
    logger.warn "Maximum depth limit (#{max_depth}) reached for store item #{id} (item: #{item&.sku}). Possible circular reference in kit structure."
    return nil
  end

  logger.tagged("Item #{item.sku} -> next_available_with_depth_limit (depth: #{current_depth})") do
    if item.is_kit?
      logger.debug 'Kit detected'
      # Get all order data
      buildable_qty = []
      buildable_date = []
      # We only analyze items with no availability
      get_kit_store_items.select { |si| si.qty_available <= 0 }.each do |ksi|
        logger.debug "Looking at next availability for #{ksi.item.sku}, store_item_id: #{ksi.id}"
        # Focus only on those components with no quantities, using depth-limited version
        if ksi_next_available = ksi.next_available_with_depth_limit(max_depth: max_depth, current_depth: current_depth + 1)
          buildable_qty << ksi_next_available.next_available_qty
          buildable_date << ksi_next_available.next_available_date
        end
        logger.debug "Next availability returned #{ksi_next_available.inspect}"
      end
      # Our most conservative approach will be the smallest quantity buildable at the latest date
      next_avail_qty = buildable_qty.min
      next_avail_date = buildable_date.compact.max
      return nil unless next_avail_qty && next_avail_date

      return OpenStruct.new(next_available_qty: buildable_qty.min,
                            next_available_date: buildable_date.compact.max).freeze

    elsif earliest_order_data = safe_on_order_first
      logger.debug 'Non kit with quantities on order'
      return OpenStruct.new(next_available_qty: earliest_order_data[:total_open],
                            next_available_date: earliest_order_data[:promised_delivery_date]).freeze
    else
      logger.debug 'Non kit, nothing on order'
      return nil
    end
  end
end

#ok_to_destroy?Boolean

Returns:

  • (Boolean)


230
231
232
# File 'app/models/store_item.rb', line 230

def ok_to_destroy?
  (usage == 0) && !is_part_of_an_active_kit?
end

#on_hand_and_committedObject



362
363
364
# File 'app/models/store_item.rb', line 362

def on_hand_and_committed
  "#{qty_on_hand} (#{qty_committed})"
end

#on_orderObject



261
262
263
# File 'app/models/store_item.rb', line 261

def on_order
  Inventory::ItemOnOrder.new.process(store_id: store_id, item_id: item_id).on_order
end

#oversize?Object

Alias for Item#oversize?

Returns:

  • (Object)

    Item#oversize?

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#packages_select_optionsObject



222
223
224
225
226
227
228
# File 'app/models/store_item.rb', line 222

def packages_select_options
  WarehousePackage
    .active
    .by_store_id(store_id)
    .select { |wp| wp.can_contain_dimensions?(shipping_dimensions, 0.1) }
    .sort_by { |p| [p.length, p.width, p.height, p.description] }.map { |wp| [wp.description, wp.id] }
end

#packagingsActiveRecord::Relation<Packaging>

Returns:

See Also:



84
# File 'app/models/store_item.rb', line 84

has_many :packagings, -> { order('packagings.number_items ASC') }, dependent: :destroy, inverse_of: :store_item

#perform_edi_actionsObject



396
397
398
399
# File 'app/models/store_item.rb', line 396

def perform_edi_actions
  # Reserved for future EDI actions when items are discontinued
  nil unless saved_change_to_is_discontinued? && is_discontinued?
end

#primary_imageObject

Alias for Item#primary_image

Returns:

  • (Object)

    Item#primary_image

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#primary_product_lineObject

Alias for Item#primary_product_line

Returns:

  • (Object)

    Item#primary_product_line

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#primary_product_line_idObject

Alias for Item#primary_product_line_id

Returns:

  • (Object)

    Item#primary_product_line_id

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#product_categoryObject

Alias for Item#product_category

Returns:

  • (Object)

    Item#product_category

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#product_linesObject

Alias for Item#product_lines

Returns:

  • (Object)

    Item#product_lines

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#public_nameObject

Alias for Item#public_name

Returns:

  • (Object)

    Item#public_name

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#qty_availableObject



209
210
211
212
213
214
215
216
217
218
219
220
# File 'app/models/store_item.rb', line 209

def qty_available
  # Computing on the fly so the value is present before the model is saved, this is also implemented in a trigger before update/inser
  # function called store_items_update_qty_available
  # pgsql equivalent: NEW.qty_available = COALESCE(NEW.permanent_qty_available,GREATEST(COALESCE(NEW.qty_on_hand,0) - COALESCE(NEW.qty_committed,0), 0));
  if qty_on_hand_changed? || qty_committed_changed? || permanent_qty_available_changed?
    # Return the value computed
    [permanent_qty_available || ((qty_on_hand || 0) - (qty_committed || 0)), 0].min
  else
    # return the activerecord version
    [permanent_qty_available, super].compact.min
  end
end

#qty_available_outside_order(order) ⇒ Object



234
235
236
237
238
239
240
241
# File 'app/models/store_item.rb', line 234

def qty_available_outside_order(order)
  committed_from_order = inventory_commits.joins(:line_item).where(line_items: { resource_type: 'Order', resource_id: order.id }).sum(:quantity)
  # use the calculated qty_committed here as it takes into account any adjusted committed count where kits are involved
  # then subtract whatever is already committed on this order
  qt_avail = permanent_qty_available # This takes preference over any other reported qty
  qt_avail ||= qty_on_hand
  (qt_avail - qty_committed) + committed_from_order
end

#qty_backorderedObject



251
252
253
254
255
256
257
258
259
# File 'app/models/store_item.rb', line 251

def qty_backordered
  @qty_bo ||= ((begin
    qty_bo
  rescue StandardError
    nil
  end) || StoreItem.all.joins('INNER JOIN catalog_items ON catalog_items.store_item_id = store_items.id INNER JOIN line_items ON line_items.catalog_item_id = catalog_items.id INNER JOIN orders ON line_items.resource_id = orders.id').where(
    "store_items.id = ? and line_items.resource_type = 'Order' and orders.state = 'crm_back_order'", id
  ).sum('line_items.quantity')).to_i
end

#qty_on_hand_in_location(location = 'AVAILABLE') ⇒ Object



199
200
201
# File 'app/models/store_item.rb', line 199

def qty_on_hand_in_location(location = 'AVAILABLE')
  item.store_items.where(location: location, store_id: store_id).pluck(:qty_on_hand)&.first
end

#qty_on_hand_with_heldObject



203
204
205
206
207
# File 'app/models/store_item.rb', line 203

def qty_on_hand_with_held
  r = qty_on_hand || 0
  r += qty_on_hand_in_location('HELD') || 0 if available_location?
  r
end

#qty_on_shelfObject



243
244
245
# File 'app/models/store_item.rb', line 243

def qty_on_shelf
  qty_on_hand_with_held - qty_shipping
end

#qty_shippingObject



247
248
249
# File 'app/models/store_item.rb', line 247

def qty_shipping
  line_items.joins(:delivery).where(deliveries: { state: Delivery::SHIPPING_STATES }).sum(:quantity)
end

#safety_stock_descriptionObject



179
180
181
182
183
184
185
# File 'app/models/store_item.rb', line 179

def safety_stock_description
  return safety_stock_fixed.to_s if safety_stock_fixed
  return "#{safety_stock_days} days" if safety_stock_days
  return "#{safety_stock_percentage * 100}%" if safety_stock_percentage

  '50%'
end

#selection_nameObject



175
176
177
# File 'app/models/store_item.rb', line 175

def selection_name
  "#{item.sku} (#{location} / #{store.short_name}) - #{item.name}"
end

#seo_descriptionObject

Alias for Item#seo_description

Returns:

  • (Object)

    Item#seo_description

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#seo_keywordsObject

Alias for Item#seo_keywords

Returns:

  • (Object)

    Item#seo_keywords

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#seo_titleObject

Alias for Item#seo_title

Returns:

  • (Object)

    Item#seo_title

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#serial_numbersActiveRecord::Relation<SerialNumber>

Returns:

See Also:



83
# File 'app/models/store_item.rb', line 83

has_many :serial_numbers, dependent: :destroy

#shipping_dimensionsObject

Alias for Item#shipping_dimensions

Returns:

  • (Object)

    Item#shipping_dimensions

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#shipping_weightObject



344
345
346
# File 'app/models/store_item.rb', line 344

def shipping_weight
  item&.base_weight || 0.0
end

#skuObject

Alias for Item#sku

Returns:

  • (Object)

    Item#sku

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#sku_and_nameObject

Alias for Item#sku_and_name

Returns:

  • (Object)

    Item#sku_and_name

See Also:



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

delegate :sku,
:sku_and_name,
:public_name,
:primary_product_line,
:primary_product_line_id,
:product_category,
:product_lines,
:is_available_to_public,
:is_circuit_check?,
:is_underlayment?,
:is_snow_melt_plaque?,
:is_roughin_kit?,
:is_cable_fit_guide?,
:is_cable_accessory?,
:is_membrane?,
:is_heating_element?,
:is_publication?,
:is_control?,
:is_service?,
:primary_image,
:seo_title,
:seo_keywords,
:seo_description,
:shipping_dimensions,
:is_kit?,
:oversize?,
to: :item

#storage_locationsActiveRecord::Relation<StorageLocation>

Returns:

See Also:



79
# File 'app/models/store_item.rb', line 79

has_and_belongs_to_many :storage_locations

#storeStore

Returns:

See Also:



74
# File 'app/models/store_item.rb', line 74

belongs_to :store, optional: true

#store_item_auditsActiveRecord::Relation<StoreItemAudit>

Returns:

See Also:



85
# File 'app/models/store_item.rb', line 85

has_many :store_item_audits, dependent: :destroy

#to_sObject



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

def to_s
  "#{item.sku} - #{store.name} - #{location} [#{id}]"
end

#unlimited_inventory?Boolean

Returns:

  • (Boolean)


167
168
169
# File 'app/models/store_item.rb', line 167

def unlimited_inventory?
  permanent_qty_available && permanent_qty_available > 0
end

#update_availabilityObject



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'app/models/store_item.rb', line 401

def update_availability
  sync_status_with_availability_date
  availability_description = "Available from #{availability_start_date || 'anytime'} until #{availability_end_date || 'anytime'}"
  messages = [availability_description]
  if is_discontinued_changed?
    if is_discontinued
      # attempts to discontinue all catalog items
      catalog_items.each(&:discontinue)
    end
    result = save ? :updated : :error
    messages += errors.full_messages
  else
    result = :unchanged
  end

  available = !is_discontinued
  messages << 'Publication has no redirection path' if !available && item.redirection_path.blank? && is_publication?
  {
    result: result,
    available: available,
    messages: messages
  }
end

#usageObject



195
196
197
# File 'app/models/store_item.rb', line 195

def usage
  line_items.count
end