Class: Item
- Inherits:
-
ApplicationRecord
show all
- Includes:
- Memery, Models::Auditable, Models::CrossLinkable, Models::Embeddable, Models::ItemAmazonHelper, Models::ItemLtreeSync, Models::ItemPackageContentHelper, Models::ItemScopable, Models::ItemSpecificationHelper, Models::Kittable, Models::LiquidMethods, Models::Publication, Models::Taggable, Models::Translatable, Models::Utilities::Html, OrderQuery, PgSearch::Model
- Defined in:
- app/models/item.rb
Overview
The Item model represents products in the inventory system.
Items can be physical goods, services, kits, or publications.
Includes pricing, specifications, product categorization, and e-commerce functionality.
== Schema Information
Table name: items(Product catalog table containing SKU and product metadata.)
Database name: primary
id :integer not null, primary key
all_pl_paths_ids :ltree default([]), is an Array
all_pl_paths_slugs :ltree is an Array
alternative_volts :string default([]), is an Array
always_available_online :boolean
amazon_asin :string(10)
amazon_optimized_listing :boolean default(FALSE), not null
base_weight :decimal(8, 4)
box2_shipping_height :decimal(6, 2)
box2_shipping_length :decimal(6, 2)
box2_shipping_weight :decimal(8, 4)
box2_shipping_width :decimal(6, 2)
box3_shipping_height :decimal(6, 2)
box3_shipping_length :decimal(6, 2)
box3_shipping_weight :decimal(8, 4)
box3_shipping_width :decimal(6, 2)
condition :string(20) default("new")
content_url :string
coo :string(255)
cycle_count_grouping :enum default("warehouse")
detailed_description_html :text
disable_heuristic_accessories :boolean default(FALSE), not null
display_treatment :integer
do_not_replenish :boolean default(FALSE), not null
dropship :boolean
feature_1 :string
feature_2 :string
feature_3 :string
feature_4 :string
feature_5 :string
featured_position :integer default(100), not null
first_sale_date :date
gross_weight :decimal(8, 4)
grouping :string
harmonization_code :string(255)
ideal_cable_spacing :float
ignore_product_line_on_item_view :boolean default(FALSE), not null
is_cable_system :boolean
is_discontinued :boolean default(FALSE), not null
is_kit :boolean default(FALSE), not null
item_category :string(255)
last_sale_date :date
legacy_accessories_specs :jsonb
legacy_image_path :string(255)
legacy_qty_out_of_stock :integer default(2), not null
legacy_ship_line_type :string(255) default("S")
legacy_specifications :string(255)
moq :integer
name :string(255)
option_name :string
oversize :boolean default(FALSE), not null
pc_path_ids :ltree
pc_path_slugs :ltree
pdf_image_descriptions :text
pdf_images_analyzed_at :datetime
popularity :integer default(0)
popularity_offset :integer default(0), not null
primary_pl_path_ids :ltree
primary_pl_path_slugs :ltree
product_packaging_flag_audited :boolean default(FALSE), not null
product_weight_flag_audited :boolean default(FALSE), not null
public_short_name :string(255)
publication_base_name :string
publication_locales :string is an Array
qty_warn_on_stock :integer default(5), not null
redirection_path :string
rendered_product_specifications :jsonb
rendered_product_specifications_at :datetime
requested_counter :integer default(0), not null
require_reservation :boolean
restricted_for_sales :boolean default(FALSE), not null
review_product_packaging_flag :boolean default(FALSE), not null
review_product_weight_flag :boolean default(FALSE), not null
search_keywords :text
search_text :text
search_text_tsv :tsvector
secondary_model_number :string
seo_description :text
seo_keywords :string
seo_title :string
shipping_class :integer default("parcel_service"), not null
shipping_height :decimal(6, 2)
shipping_length :decimal(6, 2)
shipping_weight :decimal(8, 4)
shipping_width :decimal(6, 2)
short_description :string(120)
skip_serial_number_reservation_screen :boolean
sku(WarmlyYours SKU code. Use LIKE filters for product family searches.) :string not null
sku_aliases :string default([]), is an Array
slug :string
support_priority :integer default(1000)
tax_class :string(3) not null
terms_and_conditions :text
terms_of_service :text
translations :jsonb
unique_kit_dimensions :boolean default(FALSE), not null
unlimited_inventory :boolean default(FALSE), not null
unspsc_code :string(8)
uom :string default("each"), not null
upc :string(255)
upc_on_box :boolean default(FALSE), not null
visible_for_support :boolean default(TRUE), not null
created_at :datetime
updated_at :datetime
brand_id :integer
creator_id :integer
exclusive_item_group_id :integer
literature_id :integer
new_item_id :integer
packaging_discrepancy_packing_id :integer
preferred_supplier_id :integer
primary_image_id :integer
primary_product_line_id(Primary product line foreign key for grouping and categorization.) :integer
product_category_id :integer
product_tax_code_id :integer
replacement_for_id :integer
secondary_product_category_id :integer
successor_item_id :integer
supplier_item_id :integer
updater_id :integer
upload_id :integer
Indexes
idx_items_all_pl_paths_ids (all_pl_paths_ids) USING gin
idx_items_pc_path_ids (pc_path_ids) USING gist
idx_items_primary_pl_path_ids (primary_pl_path_ids) USING gist
idx_pc_id_discontinued_kit (product_category_id,is_discontinued,is_kit)
idx_pc_id_is_disc_created (product_category_id,is_discontinued,created_at)
idx_pc_id_literature_id (product_category_id,literature_id)
idx_ppl_id_product_cat_id (primary_product_line_id,product_category_id)
idx_product_cat_id_created_at (product_category_id,created_at)
idx_product_category_id_id (product_category_id,id)
idx_upload_id (upload_id)
index_items_name_like (name)
index_items_on_all_pl_paths_slugs (all_pl_paths_slugs) USING gist
index_items_on_amazon_asin (amazon_asin) UNIQUE
index_items_on_brand_id (brand_id)
index_items_on_condition (condition)
index_items_on_exclusive_item_group_id (exclusive_item_group_id)
index_items_on_is_discontinued (is_discontinued)
index_items_on_is_kit (is_kit)
index_items_on_literature_id (literature_id)
index_items_on_new_item_id (new_item_id)
index_items_on_pc_path_slugs (pc_path_slugs) USING gist
index_items_on_primary_pl_path_slugs (primary_pl_path_slugs) USING gist
index_items_on_product_tax_code_id (product_tax_code_id)
index_items_on_publication_locales (publication_locales) USING gin
index_items_on_search_text_tsv (search_text_tsv) USING gin
index_items_on_secondary_model_number (secondary_model_number) WHERE (secondary_model_number IS NOT NULL)
index_items_on_secondary_product_category_id (secondary_product_category_id)
index_items_on_sku (sku) UNIQUE
index_items_on_sku_aliases (sku_aliases) USING gin
index_items_on_successor_item_id (successor_item_id)
index_items_on_upc_and_condition (upc,condition) UNIQUE
index_items_sku_like (sku) USING gin
items_product_category_id_condition_primary_product_line_id_idx (product_category_id,condition,primary_product_line_id)
items_rendered_product_specifications_idx (rendered_product_specifications) USING gin
items_supplier_item_id_idx (supplier_item_id)
Foreign Keys
fk_rails_... (brand_id => brands.id)
fk_rails_... (new_item_id => items.id)
fk_rails_... (packaging_discrepancy_packing_id => packings.id) ON DELETE => nullify
fk_rails_... (primary_product_line_id => product_lines.id)
fk_rails_... (product_tax_code_id => product_tax_codes.id)
fk_rails_... (secondary_product_category_id => product_categories.id)
fk_rails_... (successor_item_id => items.id)
items_product_category_id_fk (product_category_id => product_categories.id)
items_supplier_item_id_fk (supplier_item_id => supplier_items.id) ON DELETE => cascade
Defined Under Namespace
Classes: AmazonFnskuBarcode, ArchiveItem, ArticleRetriever, Auditor, AvailabilityDateChecker, Cloner, ConvertElectricalSpecs, CopyRelatedItemsFromSiblings, CustomMatCreator, CycleCountPrioritizer, CycleCountScheduler, DataMatrixBarcode, FloorPlanRetriever, HeatingElementLine, HeatingElementPlan, ImageLibraryPlPcLadder, ImageLibraryRetriever, ImageRetriever, InventoryCommitter, KitAttributeGenerator, KitComposer, KitConsolidator, MapSpecToTemplate, MarketingCycleCountScheduler, PopulateItemPopularity, PrepareNewKitComponent, ProductSpecificationsCloner, PublicationRetriever, ShippingBoxCalculator, SpecFixer, SpecificationsMasher, SpecificationsRetriever, SuggestedItemTool, SupportQrCode, SynchronizeRefurbSpecs, UpcBarcode, UpcMaker, VideoRetriever
Constant Summary
collapse
- TRANSLATABLE_ATTRIBUTES =
%i[detailed_description_html feature_1 feature_2 feature_3 feature_4 feature_5 name option_name public_short_name
search_keywords seo_description seo_keywords seo_title short_description terms_and_conditions terms_of_service].freeze
- TRANSLATION_NAMESPACE =
'ItemAttributes'
- SKU_REGEXP =
/\A\w[\w\-.]+\w\z/
- AMAZON_ASIN_REGEXP =
/\b[A-Z0-9]{10}\b/
- DISPLAY_TREATMENT_HIDE_QUANTITIES =
0
- SINGLE_QUANTITY_SKUS =
Put sku in here that needs their own line always
[].freeze
- DOUBLE_POLE_VOLTAGES =
[208.0, 240.0].freeze
- FORECAST_EXCLUDED_SKU_PREFIXES =
SKU prefixes excluded from demand forecasting (custom/discontinued/special items)
%w[cth-dis- cths- cthx- ffx- tmcs tms-sp].freeze
- SMARTPRESET_SERVICE_SKU =
%w[SMARTPRESETFORM-USA-A SMARTPRESETFORM-CAN-A SMART-PRESET-FORM-B SMART-PRESET-OJ-FORM-A SMART-PRESET-OJ-FORM-B].freeze
- CI_EXIST_CHECK_SQL =
'select 1 from catalog_items ci inner join store_items si on ci.store_item_id = si.id where si.item_id = items.id and si.is_discontinued = false'
Models::Embeddable::DEFAULT_MODEL, Models::Embeddable::MAX_CONTENT_LENGTH
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
#available_in_canada, #available_in_usa, #serve_in_locale
#do_not_compact_translation_container
#skip_ltree_sync
#amazon_variation
#creator, #updater
#amazon_marketplaces, #amazon_transparency_codes
#inbound_content_links, #outbound_content_links
#item_embeddings
#content_embeddings
#tag_records, #taggings
#kit_components, #kit_components_for_specs, #kit_parents, #kit_source_item_relations, #kit_target_item_relations, #kit_target_item_relations_for_specs
Has and belongs to many
collapse
Delegated Instance Attributes
collapse
Class Method Summary
collapse
-
.accessories ⇒ ActiveRecord::Relation<Item>
A relation of Items that are accessories.
-
.active ⇒ ActiveRecord::Relation<Item>
A relation of Items that are active.
-
.active_in_catalog_ids ⇒ ActiveRecord::Relation<Item>
A relation of Items that are active in catalog ids.
-
.all_locales ⇒ Object
NOTE: all_tags method provided by Models::Taggable concern.
-
.async_update_all_items_product_specifications(public_only: true, active_only: true, limit: nil, not_updated_since: nil) ⇒ Object
-
.available_to_public ⇒ ActiveRecord::Relation<Item>
A relation of Items that are available to public.
-
.available_to_public_for_support ⇒ ActiveRecord::Relation<Item>
A relation of Items that are available to public for support.
-
.by_primary_product_line_ids_with_descendants ⇒ ActiveRecord::Relation<Item>
A relation of Items that are by primary product line ids with descendants.
-
.canonical_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are canonical search.
-
.category_sorted ⇒ ActiveRecord::Relation<Item>
A relation of Items that are category sorted.
-
.cold_leads ⇒ ActiveRecord::Relation<Item>
A relation of Items that are cold leads.
-
.condition_select_options ⇒ Object
-
.controls ⇒ ActiveRecord::Relation<Item>
A relation of Items that are controls.
-
.countertop_heaters ⇒ ActiveRecord::Relation<Item>
A relation of Items that are countertop heaters.
-
.custom_mats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are custom mats.
-
.discontinued ⇒ ActiveRecord::Relation<Item>
A relation of Items that are discontinued.
-
.dropships ⇒ ActiveRecord::Relation<Item>
A relation of Items that are dropships.
-
.easystats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are easystats.
-
.electrical_plan_controls ⇒ ActiveRecord::Relation<Item>
A relation of Items that are electrical plan controls.
-
.excludes_forecast_skus ⇒ ActiveRecord::Relation<Item>
A relation of Items that are excludes forecast skus.
-
.floor_heating_controls ⇒ ActiveRecord::Relation<Item>
A relation of Items that are floor heating controls.
-
.floor_heating_elements ⇒ ActiveRecord::Relation<Item>
A relation of Items that are floor heating elements.
-
.for_support_portal ⇒ ActiveRecord::Relation<Item>
A relation of Items that are for support portal.
-
.forecastable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are forecastable.
-
.get_quantity_from_items_and_sqft(catalog_id, items, sqft, add_one_extra_of_last_item = false) ⇒ Object
-
.get_radiant_panel_tstat_options_for_web_and_apply_overrides(connection_method, tstat_options_override = [], available_in_catalog_id: nil) ⇒ Object
Here we want a static web page (e.g. app/views/pages/radiant-heat-panels) to use the same source of SKUs as Item does but allow it to override some of the stuff, including the order:.
-
.get_towel_warmer_tstat_options_for_web_and_apply_overrides(connection_method, tstat_options_override = [], available_in_catalog_id: nil) ⇒ Object
Here we want a static web page (e.g. app/views/pages/towel-warmer/controls) to use the same source of SKUs as Item does but allow it to override some of the stuff, including the order:.
-
.goods ⇒ ActiveRecord::Relation<Item>
A relation of Items that are goods.
-
.goods_and_services_for_public ⇒ ActiveRecord::Relation<Item>
A relation of Items that are goods and services for public.
-
.goods_visible_for_support ⇒ ActiveRecord::Relation<Item>
A relation of Items that are goods visible for support.
-
.has_floor_sensor ⇒ ActiveRecord::Relation<Item>
A relation of Items that are has floor sensor.
-
.heating_elements ⇒ ActiveRecord::Relation<Item>
A relation of Items that are heating elements.
-
.identifiers_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are identifiers search.
-
.import_translation_keys ⇒ Object
-
.in_store ⇒ ActiveRecord::Relation<Item>
A relation of Items that are in store.
-
.indexable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are indexable.
-
.install_kits ⇒ ActiveRecord::Relation<Item>
A relation of Items that are install kits.
-
.insulations ⇒ ActiveRecord::Relation<Item>
A relation of Items that are insulations.
-
.integration_kits ⇒ ActiveRecord::Relation<Item>
A relation of Items that are integration kits.
-
.item_identifier_label_sizes_select_options ⇒ Object
-
.last_custom_mat_item ⇒ Object
-
.membranes ⇒ ActiveRecord::Relation<Item>
A relation of Items that are membranes.
-
.mirror_defoggers ⇒ ActiveRecord::Relation<Item>
A relation of Items that are mirror defoggers.
-
.mirrors ⇒ ActiveRecord::Relation<Item>
A relation of Items that are mirrors.
-
.missing_shipping_dimensions ⇒ ActiveRecord::Relation<Item>
A relation of Items that are missing shipping dimensions.
-
.needs_packaging_reviewed ⇒ ActiveRecord::Relation<Item>
A relation of Items that are needs packaging reviewed.
-
.needs_weight_reviewed ⇒ ActiveRecord::Relation<Item>
A relation of Items that are needs weight reviewed.
-
.non_dropships ⇒ ActiveRecord::Relation<Item>
A relation of Items that are non dropships.
-
.non_kits ⇒ ActiveRecord::Relation<Item>
A relation of Items that are non kits.
-
.non_publications ⇒ ActiveRecord::Relation<Item>
A relation of Items that are non publications.
-
.non_shipping ⇒ ActiveRecord::Relation<Item>
A relation of Items that are non shipping.
-
.not_a_target_item ⇒ ActiveRecord::Relation<Item>
A relation of Items that are not a target item.
-
.oj_programmable_tstats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are oj programmable tstats.
-
.oj_tstats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are oj tstats.
-
.order_by_spec ⇒ ActiveRecord::Relation<Item>
A relation of Items that are order by spec.
-
.orderable_online ⇒ ActiveRecord::Relation<Item>
A relation of Items that are orderable online.
-
.out_of_stock ⇒ ActiveRecord::Relation<Item>
A relation of Items that are out of stock.
-
.packageable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are packageable.
-
.power_modules ⇒ ActiveRecord::Relation<Item>
A relation of Items that are power modules.
-
.powers ⇒ ActiveRecord::Relation<Item>
A relation of Items that are powers.
-
.primary_product_line_slug_ltree_cont ⇒ ActiveRecord::Relation<Item>
A relation of Items that are primary product line slug ltree cont.
-
.public_and_active_in_any_catalogs ⇒ ActiveRecord::Relation<Item>
A relation of Items that are public and active in any catalogs.
-
.public_and_active_in_catalog_id ⇒ ActiveRecord::Relation<Item>
A relation of Items that are public and active in catalog id.
-
.publications_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are publications search.
-
.radiant_panel_controls_dual_connect ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panel controls dual connect.
-
.radiant_panel_controls_hardwired ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panel controls hardwired.
-
.radiant_panel_controls_plug_in ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panel controls plug in.
-
.radiant_panels ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panels.
-
.radiant_panels_hardwired ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panels hardwired.
-
.ransackable_scopes(_auth_object = nil) ⇒ Object
-
.relay_panels ⇒ ActiveRecord::Relation<Item>
A relation of Items that are relay panels.
-
.remote_services ⇒ ActiveRecord::Relation<Item>
A relation of Items that are remote services.
-
.rendered_product_specs_keys ⇒ Object
-
.rendered_product_specs_options_for_select(spec_key) ⇒ Object
-
.reservable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are reservable.
-
.reviewable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are reviewable.
-
.rough_in_kits ⇒ ActiveRecord::Relation<Item>
A relation of Items that are rough in kits.
-
.sensors ⇒ ActiveRecord::Relation<Item>
A relation of Items that are sensors.
-
.services ⇒ ActiveRecord::Relation<Item>
A relation of Items that are services.
-
.shipping ⇒ ActiveRecord::Relation<Item>
A relation of Items that are shipping.
-
.sku_aliases_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are sku aliases search.
-
.sku_and_aliases_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are sku and aliases search.
-
.sku_is_membrane?(sku) ⇒ Boolean
-
.sku_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are sku search.
-
.smart_services ⇒ ActiveRecord::Relation<Item>
A relation of Items that are smart services.
-
.smartpresets ⇒ ActiveRecord::Relation<Item>
A relation of Items that are smartpresets.
-
.smartstats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are smartstats.
-
.snow_melting_elements ⇒ ActiveRecord::Relation<Item>
A relation of Items that are snow melting elements.
-
.spare_parts ⇒ ActiveRecord::Relation<Item>
A relation of Items that are spare parts.
-
.tax_classes_for_select ⇒ Object
-
.tempzones ⇒ ActiveRecord::Relation<Item>
A relation of Items that are tempzones.
-
.thermostats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are thermostats.
-
.tools ⇒ ActiveRecord::Relation<Item>
A relation of Items that are tools.
-
.towel_warmer_controls_dual_connect ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmer controls dual connect.
-
.towel_warmer_controls_hardwired ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmer controls hardwired.
-
.towel_warmer_controls_plug_in ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmer controls plug in.
-
.towel_warmers ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmers.
-
.towel_warmers_hardwired ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmers hardwired.
-
.towel_warmers_plug_in ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmers plug in.
-
.underlayments ⇒ ActiveRecord::Relation<Item>
A relation of Items that are underlayments.
-
.uom_select_options ⇒ Object
-
.upgrades ⇒ ActiveRecord::Relation<Item>
A relation of Items that are upgrades.
-
.with_all_catalog_items_discontinued ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with all catalog items discontinued.
-
.with_features ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with features.
-
.with_product_specification ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with product specification.
-
.with_stock ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with stock.
-
.with_stock_for ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with stock for.
Instance Method Summary
collapse
-
#all_images ⇒ Object
Find all images linked to this item directly or by combination of product line or category ancestry This is a simpler method than image retriever.
-
#all_my_publications(categories: nil, locale: nil, publication_category_paths: nil) ⇒ Object
-
#all_site_maps ⇒ Object
-
#all_uploads ⇒ Object
-
#any_box_changed? ⇒ Boolean
-
#async_update_rendered_product_specifications ⇒ Object
-
#authenticated_url(dimensions = nil) ⇒ Object
-
#auto_translate(reset_previous_values: false, locales: nil) ⇒ Object
-
#auto_translate_attributes(reset_previous_values: false, locales: nil) ⇒ Object
-
#auto_translate_product_specifications(reset_previous_values: false, locales: nil) ⇒ Object
-
#available_content_locales ⇒ Object
Returns all locales from the catalog this item is part of.
-
#available_token_options_from_product_specifications ⇒ Object
-
#base_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
-
#box1_changed? ⇒ Boolean
-
#box1_defined? ⇒ Boolean
-
#box2_changed? ⇒ Boolean
-
#box2_defined? ⇒ Boolean
-
#box2_shipping_dimensions_to_package ⇒ Object
-
#box2_shipping_height_converted(unit: 'in', precision: 0) ⇒ Object
-
#box2_shipping_length_converted(unit: 'in', precision: 0) ⇒ Object
-
#box2_shipping_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
-
#box2_shipping_width_converted(unit: 'in', precision: 0) ⇒ Object
-
#box3_changed? ⇒ Boolean
-
#box3_defined? ⇒ Boolean
-
#box3_shipping_dimensions_to_package ⇒ Object
-
#box3_shipping_height_converted(unit: 'in', precision: 0) ⇒ Object
-
#box3_shipping_length_converted(unit: 'in', precision: 0) ⇒ Object
-
#box3_shipping_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
-
#box3_shipping_width_converted(unit: 'in', precision: 0) ⇒ Object
-
#boxes ⇒ Object
-
#boxes_shipping_dimensions_and_weights_converted(units: { length: 'in', weight: 'lbs' }, precision: 0) ⇒ Object
-
#boxes_shipping_dimensions_and_weights_display(units: { length: 'in', weight: 'lbs' }, precision: 0) ⇒ Object
-
#boxes_to_packages ⇒ Object
-
#calculate_ideal_cable_spacing ⇒ Object
-
#calculate_is_cable_system? ⇒ Boolean
-
#calculate_item_fields ⇒ Object
-
#can_be_packaged? ⇒ Boolean
-
#canonical_path ⇒ Object
Hierarchical canonical path via product line ancestry + SKU.
-
#canonical_sku ⇒ Object
-
#canonical_url(locale: I18n.locale) ⇒ Object
Canonical URL with locale prefix.
-
#check_oversize?(carrier: nil) ⇒ Boolean
Check if item is oversize, optionally for a specific carrier When no carrier specified, checks all carriers (item is oversize if ANY carrier flags it).
-
#container_type ⇒ Object
-
#content_locales_to_render ⇒ Object
-
#controllable? ⇒ Boolean
-
#country_of_origin_iso3 ⇒ Object
-
#country_of_origin_name ⇒ Object
-
#create_template_specifications ⇒ Object
-
#current_supplier_cost ⇒ Object
-
#deep_dup ⇒ Object
-
#default_short_description ⇒ Object
-
#detailed_description ⇒ Object
Creates a text description from the html rendered description.
-
#discontinue_self_and_dependents ⇒ Object
-
#discontinue_store_and_catalog_items ⇒ Object
-
#ean13 ⇒ Object
-
#effective_option_name ⇒ Object
-
#effective_seo_description(char_limit: nil) ⇒ Object
-
#effective_seo_keywords ⇒ Object
-
#effective_seo_title(skip_sku: false) ⇒ Object
-
#express_weight_converted(weight_in_lbs, unit: 'lbs', precision: nil) ⇒ Object
-
#facet ⇒ Object
-
#facet_sort_keys ⇒ Object
-
#facet_tokens ⇒ Object
-
#features ⇒ Object
Features are a sub category of specs with feature priority set.
-
#fix_name ⇒ Object
-
#fixture_key ⇒ Object
-
#freight_class ⇒ Object
-
#generate_refurbished_sku ⇒ Object
-
#get_quantity_from_sqft(input_sqft, add_one_extra = false) ⇒ Object
-
#gross_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
-
#gtin13 ⇒ Object
-
#gtin_type ⇒ Object
-
#gtin_type_friendly ⇒ Object
-
#harmonization_code_hs6(with_dot = true) ⇒ Object
Returns the first 6 digits of the harmonization code (HS6).
-
#has_ean13? ⇒ Boolean
-
#has_features? ⇒ Boolean
-
#has_heating_system? ⇒ Boolean
-
#has_stock? ⇒ Boolean
-
#has_stock_in_store?(st_id) ⇒ Boolean
-
#image_url(dimensions, options = {}) ⇒ Object
-
#image_url2(options = {}) ⇒ Object
Use this new form that passes the options straight to the url generator.
-
#import_translation_keys ⇒ Object
This method will import existing translations or link to existing ones.
-
#increment_request_counter ⇒ Object
-
#inherited_features ⇒ Object
Seek inherited features from the primary product line To inherit features, a product line's product category must be empty or matching e.g Tempzone Floor Heating has Heating Elements as a product category then items belonging to Tempzone Floor Heating must also be heating elements to inherit its features.
-
#is_available_to_public? ⇒ Boolean
-
#is_cable_accessory? ⇒ Boolean
-
#is_cable_fit_guide? ⇒ Boolean
-
#is_cable_heating_element? ⇒ Boolean
-
#is_cable_tape? ⇒ Boolean
-
#is_cerazorb? ⇒ Boolean
-
#is_circuit_check? ⇒ Boolean
-
#is_cold_lead? ⇒ Boolean
-
#is_cork? ⇒ Boolean
-
#is_discontinued_or_publication ⇒ Object
Helper for validation: skips weight checks for discontinued items and publications.
-
#is_economy_snow_melt_control? ⇒ Boolean
-
#is_floor_heating_product? ⇒ Boolean
-
#is_goods? ⇒ Boolean
-
#is_heating_element? ⇒ Boolean
-
#is_install_kit? ⇒ Boolean
-
#is_junction_box? ⇒ Boolean
-
#is_led_mirror? ⇒ Boolean
-
#is_legacy_relay? ⇒ Boolean
-
#is_made_to_order_led_mirror? ⇒ Boolean
-
#is_membrane? ⇒ Boolean
-
#is_mirror? ⇒ Boolean
-
#is_mirror_defogger? ⇒ Boolean
-
#is_oj_programmable_tstat? ⇒ Boolean
-
#is_oj_programmable_wifi_tstat? ⇒ Boolean
-
#is_pipe_freeze_protection? ⇒ Boolean
-
#is_power? ⇒ Boolean
-
#is_power_module? ⇒ Boolean
-
#is_radiant_panel? ⇒ Boolean
-
#is_radiant_panel_dual_connect? ⇒ Boolean
-
#is_radiant_panel_hardwired? ⇒ Boolean
-
#is_radiant_panel_hardwired_control? ⇒ Boolean
-
#is_radiant_panel_plug_in? ⇒ Boolean
-
#is_radiant_panel_plug_in_control? ⇒ Boolean
-
#is_radiator? ⇒ Boolean
-
#is_refurbished? ⇒ Boolean
-
#is_reviewable? ⇒ Boolean
-
#is_roughin_kit? ⇒ Boolean
-
#is_self_regulating_cable? ⇒ Boolean
-
#is_service? ⇒ Boolean
-
#is_sh_cable? ⇒ Boolean
-
#is_shipping? ⇒ Boolean
-
#is_slab_heat_mat? ⇒ Boolean
-
#is_sm_cable? ⇒ Boolean
-
#is_smartstat? ⇒ Boolean
-
#is_snow_melt_plaque? ⇒ Boolean
-
#is_snow_melting_control? ⇒ Boolean
-
#is_snow_melting_mat? ⇒ Boolean
-
#is_snow_melting_product? ⇒ Boolean
-
#is_spare_parts? ⇒ Boolean
-
#is_thermalsheet? ⇒ Boolean
-
#is_thermostat? ⇒ Boolean
-
#is_towel_warmer? ⇒ Boolean
-
#is_towel_warmer_dual_connect? ⇒ Boolean
-
#is_towel_warmer_hardwired? ⇒ Boolean
-
#is_towel_warmer_hardwired_control? ⇒ Boolean
-
#is_towel_warmer_plug_in? ⇒ Boolean
-
#is_towel_warmer_plug_in_control? ⇒ Boolean
-
#is_tz_cable? ⇒ Boolean
-
#is_tz_ruler_cable? ⇒ Boolean
-
#is_tz_thin_cable? ⇒ Boolean
-
#is_underlayment? ⇒ Boolean
-
#item_available_locales ⇒ Object
-
#item_group_name(context: :sales) ⇒ Object
-
#item_grouping_info(include_self: false, facet_filters: {}, context: nil) ⇒ Object
-
#item_state ⇒ Object
-
#loaded_rendered_product_specifications ⇒ Object
-
#make_upc_code ⇒ Object
-
#missing_catalogs ⇒ Object
-
#most_recent_revision(public_only = true) ⇒ Object
-
#new_revision_sku ⇒ Object
-
#non_refurbished_sku ⇒ Object
-
#order_count ⇒ Object
-
#orderable_online? ⇒ Boolean
-
#orderable_online_in_locale?(locale = I18n.locale) ⇒ Boolean
-
#past_model? ⇒ Boolean
-
#pc_descendant_of_path?(path_slug) ⇒ Boolean
Check if item's product category is or descends from the given path Uses pc_path_slugs stored directly on item (no DB query) Example: item.pc_descendant_of_path?(LtreePaths::PC_TOWEL_WARMERS).
-
#pl_descendant_of_path?(path_slug) ⇒ Boolean
Check if item's primary product line is or descends from the given path Uses ltree path stored directly on item (no DB query) Example: item.pl_descendant_of_path?(LtreePaths::PL_FLOOR_HEATING).
-
#plan_sensitive? ⇒ Boolean
Plan sensitive items are those tied up to the geometry of an installation plan.
-
#primary_image_url(options = {}) ⇒ Object
Returns the primary image URL for display (e.g., cart toast).
-
#primary_product_line_slug_ltree ⇒ Object
-
#product_category_ancestry_ids ⇒ Object
-
#product_category_name ⇒ Object
-
#product_category_path ⇒ Object
Returns ltree path for product category grouping (preferred - no DB lookup needed).
-
#product_category_priority ⇒ Object
-
#product_category_slug ⇒ Object
Returns product category URL for grouping (legacy - use product_category_path for new code).
-
#product_category_url ⇒ Object
-
#product_line_ancestry_ids ⇒ Object
-
#product_line_for_heating_system ⇒ Object
-
#product_line_for_public_sales_portal ⇒ Object
-
#product_line_for_review ⇒ Object
-
#product_line_ids_constellation ⇒ Object
This pulls all product line id including inherited for this item, for the root system, and complimentary_product_line_ids.
-
#product_specifications(filters = {}) ⇒ Object
Retrieves all product specifications available to a given item, this does not retrieve rendered specs but the specs definition used to build them.
-
#prune_empty_specs ⇒ Object
-
#prune_unused_translations ⇒ Object
-
#public_description_html ⇒ Object
-
#public_description_text ⇒ Object
-
#public_file_name ⇒ Object
-
#public_name ⇒ Object
-
#public_specifications ⇒ Object
Only specifications intended for the public are retrieved.
-
#purge_edge_cache(include_product_line: false) ⇒ Object
-
#quantities_indicator_visible? ⇒ Boolean
-
#refresh_google_feed ⇒ Object
-
#relatives ⇒ Object
Find all direct siblings and relatives items descendending the product category and product line tree.
-
#render_product_specifications(tokens: nil) ⇒ Object
-
#rendered_detailed_description_html ⇒ Object
-
#rendered_product_specifications_grouped(locale: Mobility.locale, filter_grouping: nil, sort_order: []) ⇒ Object
-
#requires_distributor_id_code? ⇒ Boolean
-
#retrieve_faqs ⇒ Object
-
#revised_publication_for_public ⇒ Object
-
#rma_reason_codes(include_advanced: true) ⇒ Object
-
#run_shipping_audit(carrier: nil) ⇒ Array<Shipping::PackageAuditor>
Run shipping audit for all boxes, optionally for a specific carrier.
-
#safe_name ⇒ Object
Returns a name suitable for edit feed with any special character that will destroy a csv formatting.
-
#sales_product_line_path ⇒ Object
Returns ltree path for sales product line (preferred - reuses already-loaded ProductLine).
-
#sales_product_line_slug ⇒ Object
-
#selection_name ⇒ Object
-
#set_grouping(force: false) ⇒ Object
Records a grouping string identifier, this speeds up variant grouping lookup for google and reviews.io api calls.
-
#set_packaged_items_md5_hash(options = {}) ⇒ Object
-
#shift_images(locale: 'en', prefix: nil) ⇒ Object
Shifts images up to fill empty slots in the image type order for this item This method will move images to fill gaps in the sequence, maintaining the proper order.
-
#shipping_dimensions(unit: 'in', precision: 0) ⇒ Object
unit_system SI = Standard International, e.g Metric ucs = United States Customary Unit.
-
#shipping_dimensions_display ⇒ Object
-
#shipping_dimensions_to_package ⇒ Object
-
#shipping_height_converted(unit: 'in', precision: 0) ⇒ Object
-
#shipping_length_converted(unit: 'in', precision: 0) ⇒ Object
-
#shipping_volume ⇒ Object
-
#shipping_volume_in_cubic_feet ⇒ Object
-
#shipping_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
-
#shipping_width_converted(unit: 'in', precision: 0) ⇒ Object
-
#ships_in_single_box? ⇒ Boolean
-
#ships_via_crate? ⇒ Boolean
-
#ships_via_freight? ⇒ Boolean
-
#siblings ⇒ Object
-
#single_item_packings_from_md5 ⇒ Object
Finds packings of this item but only for a single quantity.
-
#sku_and_name ⇒ Object
-
#specifications ⇒ Object
Retrieves rendered specifications as an object.
-
#specifications_for_item_label ⇒ Object
-
#specifications_grouped ⇒ Object
-
#star? ⇒ Boolean
Makes a star appear in the document library.
-
#supplier_skus ⇒ Object
-
#support_product_line_path ⇒ Object
Returns ltree path for support product line (preferred - reuses already-loaded ProductLine).
-
#support_product_line_slug ⇒ Object
-
#tags_display ⇒ Object
-
#third_party_skus(limit: 1) ⇒ Object
-
#thumbnail_url(size: '30x30') ⇒ Object
-
#to_liquid ⇒ Object
-
#to_packdims ⇒ Object
-
#to_s ⇒ Object
-
#total_shipping_weight ⇒ Object
-
#total_shipping_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
-
#total_shipping_weight_of_kit_components ⇒ Object
-
#update_boxes_from_packages(packages) ⇒ Object
-
#update_rendered_product_specifications(user_id: nil, tokens: nil, locales: nil, skip_google_feed: true) ⇒ Object
-
#url_paths ⇒ Object
-
#variants(include_self: false) ⇒ Object
A more strict version of relatives, we don't look at descendants, only exact matches.
-
#versions_for_audit_trail(_params = {}) ⇒ Object
-
#warm_cache(include_product_line: true) ⇒ Object
Finds the associated sitemap (url) end points of this item and its product line Perform a cache warmup.
-
#will_be_restricted_for_sales? ⇒ Boolean
#all_amazon_image_profiles, #amazon_description, #amazon_description=, #amazon_feature_1, #amazon_feature_1=, #amazon_feature_2, #amazon_feature_2=, #amazon_feature_3, #amazon_feature_3=, #amazon_feature_4, #amazon_feature_4=, #amazon_feature_5, #amazon_feature_5=, #amazon_feature_6, #amazon_feature_6=, #amazon_feature_7, #amazon_feature_7=, #amazon_feature_8, #amazon_feature_8=, #amazon_feature_9, #amazon_feature_9=, #amazon_features, #amazon_generic_keyword, #amazon_generic_keyword=, #amazon_product_types, #amazon_target_keywords, #amazon_title, #amazon_title=, #create_or_set_amazon_spec_value, #description_for_amazon, #is_amazon_item?, #reset_amazon_fields, #sync_amazon_asins, #title_for_amazon, with_asin
#content_links_count, #linked_content, #linked_posts, #linked_publications, #linked_showcases, #linked_videos
ai_search_publications, ai_search_warning?, #alias_sku, #analyze_pdf_images!, #available_service_locales, #available_to_all_locales?, #check_redirection_path, clear_ai_search_warning!, #compose_name_with_languages, #content_for_embedding, #cover_image_url, #create_primary_image_from_pdf, #default_publication_logistics, embeddable_content_types, #embeddable_locales, #embedding_content_changed?, #fast_country_discontinue, #file_name_for_download, #friendly_locale_names, generate_openai_query_embedding, #generate_publication_name, #has_literature_changed?, hybrid_search_publications, #locale_for_embedding, #needs_vision_analysis?, #perform_country_discontinue, #product_category_must_be_publication, #publication_available_locales, #publication_base_name_clean_of_language, #publication_pdf_changed?, #publication_sku_check, #publication_url, #publication_visible_to_public?, publications, publications_for_online_portal, publications_for_public, publications_for_public_in_store, publications_for_sales_portal, publications_for_support_portal, #publish_pdf_changed_event, #retrieve_publications_search_text, #secondary_product_category_must_not_be_publication, #set_search_text, #should_queue_embedding?, with_publication_attached
rrf_ranked_relation
#content_for_embedding, embeddable_content_types, #embeddable_locales, #embedding_content_hash, embedding_partition_class, #embedding_stale?, #embedding_type_name, #embedding_vector, #find_content_embedding, #find_similar, #generate_all_embeddings!, #generate_chunked_embeddings!, #generate_embedding!, #has_embedding?, #locale_for_embedding, #needs_chunking?, regenerate_all_embeddings, semantic_search
#add_tag, all_tags, #has_tag?, normalize_tag_names, not_tagged_with, #remove_tag, #tag_list, #tag_list=, #taggable_type_for_tagging, tagged_with, #tags, #tags=, tags_cloud, tags_exclude, tags_include, with_all_tags, with_any_tags, without_all_tags, without_any_tags
#compact_translation_container, skip_compaction_for
#build_package_content_entries, #condense_group_to_char_limit, #group_entries_for_max_unique_items, #package_content_html, #package_content_short_name, #package_contents_array, #package_contents_friendly
#amps, #amps=, #amps_static?, #area, #aspect, #btu_per_hour, #calculate_amps, #calculate_coverage_for_membrane, #calculate_geometric_sqft, #calculate_heated_area_for_panels, #calculate_heated_area_sqft_for_panels, #calculate_heated_area_sqm_for_panels, #calculate_kilowatts, #calculate_number_of_fixing_strips_at_3_in, #calculate_number_of_fixing_strips_at_4_in, #calculate_number_of_fixing_strips_at_5_in, #calculate_number_of_fixing_strips_at_cable_spacing, #calculate_ohms, #calculate_ohms_per_ft, #calculate_size_2d, #calculate_size_2d_ft, #calculate_size_2d_in, #calculate_size_2d_metric_cm, #calculate_size_2d_metric_mm, #calculate_size_3d, #calculate_size_3d_ft, #calculate_size_3d_in, #calculate_size_3d_metric_cm, #calculate_size_3d_metric_mm, #calculate_sqft, #calculate_thermal_power_per_hour, #calculate_volts, #calculate_watts, #calculate_watts_per_sqft, #can_use_amps_for_calculation?, #can_use_ohms_for_calculation?, #can_use_voltage_for_calculation?, #can_use_watts_for_calculation?, #cold_lead_length, #cold_lead_length=, #control_capacity, #coverage_at_3_5_in, #coverage_at_3_75_in, #coverage_at_3_in, #coverage_at_4_5_in, #coverage_at_4_in, #coverage_at_5_in, #coverage_formatted, #create_or_set_spec_value, #finish, #has_spec?, #is_dual_voltage?, #length, #length=, #length_formatted, #linear_ft, #maximum_current, #num_poles, #ohms, #ohms=, #ohms_law_calculator, #ohms_static?, #plan_grouping, #plan_grouping=, #set_spec_value, #spec, #spec_image, #spec_output, #spec_refresh_in_progress, #spec_unit, #spec_value, #specs_for_identifier_label, #sqft, #sqft=, #token_specs_values_for_liquid, #voltage, #voltage=, #voltage_formatted, #voltage_static?, #volts, #volts=, #vop, #watts, #watts=, #watts_formatted, #watts_per_linear_feet, #watts_per_sqft, #watts_per_sqft_at_3_5_in_spacing, #watts_per_sqft_at_3_75_in_spacing, #watts_per_sqft_at_3_in_spacing, #watts_per_sqft_at_4_5_in_spacing, #watts_per_sqft_at_4_in_spacing, #watts_per_sqft_at_5_in_spacing, #watts_static?, #width, #width=, #width_by_length_text, #width_by_length_text_2, #width_by_length_text_3, #width_formatted
#box_contents, #consolidate_linked_kits, #get_kit_item_ids, #get_kit_items, #get_multi_box_item_contents_item_id_hash_array, #get_self_and_kit_item_ids, #get_self_and_kit_items, #is_part_of_a_kit?, #is_part_of_an_active_kit?, #kit_contents, kits, #shipping_dimensions_changed?, #store_item_for
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
#build_ltree_paths_from_associations, bulk_sync_ltree_paths!, sync_items_for_product_categories!, sync_items_for_product_lines!, #sync_ltree_paths!
by_product_category_id, by_product_category_id_direct, by_product_category_path, by_product_category_path_exact, by_product_category_url, by_product_category_url_exact, by_product_line_id, by_product_line_path, by_product_line_path_full, by_product_line_url, by_product_line_url_full, not_by_product_category_id, not_by_product_line_id
ransackable_associations, ransackable_attributes, ransortable_attributes, #to_relation
#publish_event
Instance Attribute Details
#amazon_asin ⇒ Object
312
313
314
|
# File 'app/models/item.rb', line 312
validates :amazon_asin, uniqueness: true,
format: { with: AMAZON_ASIN_REGEXP, message: 'must be 10 alphanumeric characters starting with a letter' },
allow_blank: true
|
#name_en ⇒ Object
Here we check specifically for the english name to be present
Validations:
307
|
# File 'app/models/item.rb', line 307
validates :name_en, :tax_class, presence: true
|
#refresh_specs ⇒ Object
These are shortcuts for publications
215
216
217
|
# File 'app/models/item.rb', line 215
def refresh_specs
@refresh_specs
end
|
#skip_item_calc ⇒ Object
Returns the value of attribute skip_item_calc.
632
633
634
|
# File 'app/models/item.rb', line 632
def skip_item_calc
@skip_item_calc
end
|
#sku ⇒ Object
rubocop:disable Rails/I18nLocaleTexts
Validations:
-
Presence
-
Uniqueness
-
Format
({ with: SKU_REGEXP, message: 'must only contain A-Z 0-9 - _ . and must end with a number or letter' })
309
310
311
|
# File 'app/models/item.rb', line 309
validates :sku, presence: true,
uniqueness: true,
format: { with: SKU_REGEXP, message: 'must only contain A-Z 0-9 - _ . and must end with a number or letter' }
|
#tax_class ⇒ Object
Here we check specifically for the english name to be present
Validations:
307
|
# File 'app/models/item.rb', line 307
validates :name_en, :tax_class, presence: true
|
#update_search_text ⇒ Object
Returns the value of attribute update_search_text.
632
633
634
|
# File 'app/models/item.rb', line 632
def update_search_text
@update_search_text
end
|
#update_upc ⇒ Object
Returns the value of attribute update_upc.
632
633
634
|
# File 'app/models/item.rb', line 632
def update_upc
@update_upc
end
|
Class Method Details
.accessories ⇒ ActiveRecord::Relation<Item>
A relation of Items that are accessories. Active Record Scope
409
|
# File 'app/models/item.rb', line 409
scope :accessories, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_ACCESSORIES)) }
|
.active ⇒ ActiveRecord::Relation<Item>
A relation of Items that are active. Active Record Scope
365
|
# File 'app/models/item.rb', line 365
scope :active, -> { where(is_discontinued: false) }
|
.active_in_catalog_ids ⇒ ActiveRecord::Relation<Item>
A relation of Items that are active in catalog ids. Active Record Scope
511
512
513
514
515
|
# File 'app/models/item.rb', line 511
scope :active_in_catalog_ids, ->(*catalog_ids) {
catalog_ids = [catalog_ids].flatten.filter_map(&:presence).uniq
active
.where("exists(#{CI_EXIST_CHECK_SQL} and ci.catalog_id IN (?))", catalog_ids.flatten)
}
|
.all_locales ⇒ Object
NOTE: all_tags method provided by Models::Taggable concern
661
662
663
|
# File 'app/models/item.rb', line 661
def self.all_locales
pluck(Arel.sql('distinct unnest(publication_locales) as locale')).compact.sort
end
|
.async_update_all_items_product_specifications(public_only: true, active_only: true, limit: nil, not_updated_since: nil) ⇒ Object
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
|
# File 'app/models/item.rb', line 2033
def self.async_update_all_items_product_specifications(public_only: true, active_only: true, limit: nil, not_updated_since: nil)
items = Item.non_publications
items = items.available_to_public if public_only items = items.active unless public_only && !active_only
items = items.where('rendered_product_specifications_at IS NULL OR rendered_product_specifications_at < ?', not_updated_since) if not_updated_since
items = items.limit(limit) if limit
items.distinct.pluck(:id).each { |iid| ItemAttributeWorker.perform_async(iid) }
end
|
.available_to_public ⇒ ActiveRecord::Relation<Item>
A relation of Items that are available to public. Active Record Scope
367
|
# File 'app/models/item.rb', line 367
scope :available_to_public, -> { active.public_and_active_in_catalog_id(1, 2) }
|
.available_to_public_for_support ⇒ ActiveRecord::Relation<Item>
A relation of Items that are available to public for support. Active Record Scope
369
|
# File 'app/models/item.rb', line 369
scope :available_to_public_for_support, -> { condition_new.where(visible_for_support: true).where(primary_product_line_id: ProductLine.for_support_portal.select(:id)) }
|
.by_primary_product_line_ids_with_descendants ⇒ ActiveRecord::Relation<Item>
A relation of Items that are by primary product line ids with descendants. Active Record Scope
527
528
529
530
531
|
# File 'app/models/item.rb', line 527
scope :by_primary_product_line_ids_with_descendants, ->(*pl_ids) {
ids = [pl_ids].flatten.compact.map(&:to_i)
paths = ProductLine.where(id: ids).pluck(:ltree_path_ids).compact
paths.empty? ? none : where(Item[:primary_pl_path_ids].ltree_descendant(paths))
}
|
.canonical_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are canonical search. Active Record Scope
439
440
441
442
|
# File 'app/models/item.rb', line 439
scope :canonical_search, ->(sku) {
escaped = Regexp.escape(sku.to_s)
where('items.sku ~ ?', "^#{escaped}-[A-Za-z]{1,2}$").order(sku: :desc)
}
|
.category_sorted ⇒ ActiveRecord::Relation<Item>
A relation of Items that are category sorted. Active Record Scope
.cold_leads ⇒ ActiveRecord::Relation<Item>
A relation of Items that are cold leads. Active Record Scope
.condition_select_options ⇒ Object
909
910
911
|
# File 'app/models/item.rb', line 909
def self.condition_select_options
Item.conditions.keys.map { |c| [c.humanize.titleize, c] }
end
|
.controls ⇒ ActiveRecord::Relation<Item>
A relation of Items that are controls. Active Record Scope
387
|
# File 'app/models/item.rb', line 387
scope :controls, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_CONTROLS)) }
|
.countertop_heaters ⇒ ActiveRecord::Relation<Item>
A relation of Items that are countertop heaters. Active Record Scope
.custom_mats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are custom mats. Active Record Scope
.discontinued ⇒ ActiveRecord::Relation<Item>
A relation of Items that are discontinued. Active Record Scope
366
|
# File 'app/models/item.rb', line 366
scope :discontinued, -> { where(is_discontinued: true) }
|
.dropships ⇒ ActiveRecord::Relation<Item>
A relation of Items that are dropships. Active Record Scope
463
|
# File 'app/models/item.rb', line 463
scope :dropships, -> { where(dropship: true) }
|
.easystats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are easystats. Active Record Scope
.electrical_plan_controls ⇒ ActiveRecord::Relation<Item>
A relation of Items that are electrical plan controls. Active Record Scope
478
479
480
481
482
|
# File 'app/models/item.rb', line 478
scope :electrical_plan_controls, -> {
controls
.where(Item[:primary_pl_path_slugs].ltree_descendant(LtreePaths::PL_ALL_CONTROLS))
.where.not(sku: %w[RLY-12PL SCE-ELEC])
}
|
.excludes_forecast_skus ⇒ ActiveRecord::Relation<Item>
A relation of Items that are excludes forecast skus. Active Record Scope
434
|
# File 'app/models/item.rb', line 434
scope :excludes_forecast_skus, -> { where.not('items.sku SIMILAR TO ?', "(#{FORECAST_EXCLUDED_SKU_PREFIXES.join('|')})%") }
|
.floor_heating_controls ⇒ ActiveRecord::Relation<Item>
A relation of Items that are floor heating controls. Active Record Scope
.floor_heating_elements ⇒ ActiveRecord::Relation<Item>
A relation of Items that are floor heating elements. Active Record Scope
407
|
# File 'app/models/item.rb', line 407
scope :floor_heating_elements, -> { heating_elements.where(Item[:primary_pl_path_slugs].ltree_descendant(LtreePaths::PL_FLOOR_HEATING)) }
|
.for_support_portal ⇒ ActiveRecord::Relation<Item>
A relation of Items that are for support portal. Active Record Scope
.forecastable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are forecastable. Active Record Scope
436
|
# File 'app/models/item.rb', line 436
scope :forecastable, -> { active.non_kits.non_publications.non_shipping.excludes_forecast_skus }
|
.get_quantity_from_items_and_sqft(catalog_id, items, sqft, add_one_extra_of_last_item = false) ⇒ Object
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
|
# File 'app/models/item.rb', line 1465
def self.get_quantity_from_items_and_sqft(catalog_id, items, sqft, = false)
res_arr = []
cost_arr = []
2.times do |pass|
area = sqft
res = {}
cost = 0.0
last_i = items.length - 1
items.each_with_index do |item, i|
= if && (i == last_i) && pass.zero? 1
else
0
end
qty =
if item.sqft.to_f > 0.0
qty += if (i == last_i) || ((pass == 1) && (items[i + 1].sqft < sqft)) (area / item.sqft).ceil.to_i else
(area / item.sqft).floor.to_i end
end
next unless qty.positive?
res[item.sku] = qty
area -= (item.sqft.to_f * qty)
ci = item.catalog_items.detect { |ci2| ci2.catalog_id == catalog_id } || item.catalog_items.first
cost += (qty * ci.amount.to_f)
end
res_arr << res
cost_arr << cost
end
res_arr[cost_arr.index(cost_arr.min)]
end
|
.get_radiant_panel_tstat_options_for_web_and_apply_overrides(connection_method, tstat_options_override = [], available_in_catalog_id: nil) ⇒ Object
Here we want a static web page (e.g. app/views/pages/radiant-heat-panels) to use the same source of SKUs as Item does but allow it to override some of the stuff, including the order:
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
|
# File 'app/models/item.rb', line 2365
def self.get_radiant_panel_tstat_options_for_web_and_apply_overrides(connection_method, tstat_options_override = [], available_in_catalog_id: nil)
available_in_catalog_id = Catalog.locale_to_catalog_id(I18n.locale) if available_in_catalog_id.nil?
num_overrides = tstat_options_override.size base_item_scope = Item.public_and_active_in_catalog_id(available_in_catalog_id)
controls = if connection_method == :hardwired
base_item_scope.radiant_panel_controls_hardwired
else
base_item_scope.radiant_panel_controls_plug_in
end
res = controls.map do |item|
tstat_options_override.detect do |h|
h[:sku] == item.sku
end || { sku: item.sku, name: item.name, image: item.primary_image.image_url(width: 300, height: 300) }
end
res.sort_by { |h| tstat_options_override.index(h) || num_overrides }
end
|
.get_towel_warmer_tstat_options_for_web_and_apply_overrides(connection_method, tstat_options_override = [], available_in_catalog_id: nil) ⇒ Object
Here we want a static web page (e.g. app/views/pages/towel-warmer/controls) to use the same source of SKUs as Item does but allow it to override some of the stuff, including the order:
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
|
# File 'app/models/item.rb', line 2346
def self.get_towel_warmer_tstat_options_for_web_and_apply_overrides(connection_method, tstat_options_override = [], available_in_catalog_id: nil)
available_in_catalog_id = Catalog.locale_to_catalog_id(I18n.locale) if available_in_catalog_id.nil?
num_overrides = tstat_options_override.size base_item_scope = Item.public_and_active_in_catalog_id(available_in_catalog_id)
controls = if connection_method == :hardwired
base_item_scope.towel_warmer_controls_hardwired
else
base_item_scope.towel_warmer_controls_plug_in
end
res = controls.map do |item|
tstat_options_override.detect do |h|
h[:sku] == item.sku
end || { sku: item.sku, name: item.name, image: item.primary_image.image_url(width: 300, height: 300) }
end
res.sort_by { |h| tstat_options_override.index(h) || num_overrides }
end
|
.goods ⇒ ActiveRecord::Relation<Item>
A relation of Items that are goods. Active Record Scope
413
|
# File 'app/models/item.rb', line 413
scope :goods, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_GOODS)) }
|
.goods_and_services_for_public ⇒ ActiveRecord::Relation<Item>
A relation of Items that are goods and services for public. Active Record Scope
437
|
# File 'app/models/item.rb', line 437
scope :goods_and_services_for_public, -> { available_to_public.active.non_publications }
|
.goods_visible_for_support ⇒ ActiveRecord::Relation<Item>
A relation of Items that are goods visible for support. Active Record Scope
457
|
# File 'app/models/item.rb', line 457
scope :goods_visible_for_support, -> { non_publications.condition_new.goods.where(visible_for_support: true) }
|
.has_floor_sensor ⇒ ActiveRecord::Relation<Item>
A relation of Items that are has floor sensor. Active Record Scope
477
|
# File 'app/models/item.rb', line 477
scope :has_floor_sensor, -> { with_product_specification(:has_floor_sensor, 'y') }
|
.heating_elements ⇒ ActiveRecord::Relation<Item>
A relation of Items that are heating elements. Active Record Scope
.identifiers_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are identifiers search. Active Record Scope
543
544
545
546
547
548
549
550
551
552
553
554
555
|
# File 'app/models/item.rb', line 543
scope :identifiers_search, ->(identifier) {
where(%{
items.sku ILIKE :identifier_wild
OR items.upc = :identifier
OR items.upc = RIGHT(:identifier,12)
OR items.secondary_model_number ILIKE :identifier_wild
OR items.amazon_asin = :identifier
OR items.sku_aliases @> ARRAY[:identifier]::varchar[]
OR supplier_items.supplier_sku ILIKE :identifier_wild
},
identifier_wild: "#{identifier}%",
identifier:).left_outer_joins(:supplier_item).order(sanitize_sql_for_order([Arel.sql('items.is_discontinued = true,items.sku <-> ?'), identifier]))
}
|
.import_translation_keys ⇒ Object
1869
1870
1871
|
# File 'app/models/item.rb', line 1869
def self.import_translation_keys
Item.where.not(translations: {}).find_each(&:import_translation_keys)
end
|
.in_store ⇒ ActiveRecord::Relation<Item>
A relation of Items that are in store. Active Record Scope
418
419
420
|
# File 'app/models/item.rb', line 418
scope :in_store, ->(*store_ids) {
where('exists(select id from store_items where store_items.item_id = items.id and store_items.store_id IN (?) and store_items.is_discontinued = false)', [store_ids].flatten)
}
|
.indexable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are indexable. Active Record Scope
506
|
# File 'app/models/item.rb', line 506
scope :indexable, -> { not_tagged_with('Noindex') }
|
.install_kits ⇒ ActiveRecord::Relation<Item>
A relation of Items that are install kits. Active Record Scope
.insulations ⇒ ActiveRecord::Relation<Item>
A relation of Items that are insulations. Active Record Scope
.integration_kits ⇒ ActiveRecord::Relation<Item>
A relation of Items that are integration kits. Active Record Scope
.item_identifier_label_sizes_select_options ⇒ Object
921
922
923
|
# File 'app/models/item.rb', line 921
def self.item_identifier_label_sizes_select_options
[['2.3125" x 4" (S-8505)', 'regular'], ['4" x 6" (S-17044)', 'large']]
end
|
.last_custom_mat_item ⇒ Object
.membranes ⇒ ActiveRecord::Relation<Item>
A relation of Items that are membranes. Active Record Scope
486
|
# File 'app/models/item.rb', line 486
scope :membranes, -> { where(sku: ItemConstants::MEMBRANE_SKUS) }
|
.mirror_defoggers ⇒ ActiveRecord::Relation<Item>
A relation of Items that are mirror defoggers. Active Record Scope
.mirrors ⇒ ActiveRecord::Relation<Item>
A relation of Items that are mirrors. Active Record Scope
386
|
# File 'app/models/item.rb', line 386
scope :mirrors, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_MIRRORS)) }
|
.missing_shipping_dimensions ⇒ ActiveRecord::Relation<Item>
A relation of Items that are missing shipping dimensions. Active Record Scope
373
374
375
376
377
378
379
380
381
|
# File 'app/models/item.rb', line 373
scope :missing_shipping_dimensions, -> {
arel = Item.arel_table
goods.where(
arel[:shipping_length].lteq(0).or(arel[:shipping_length].eq(nil))
.or(arel[:shipping_width].lteq(0)).or(arel[:shipping_width].eq(nil))
.or(arel[:shipping_height].lteq(0)).or(arel[:shipping_height].eq(nil))
.or(arel[:shipping_weight].lteq(0)).or(arel[:shipping_weight].eq(nil))
)
}
|
.needs_packaging_reviewed ⇒ ActiveRecord::Relation<Item>
A relation of Items that are needs packaging reviewed. Active Record Scope
541
|
# File 'app/models/item.rb', line 541
scope :needs_packaging_reviewed, -> { where(review_product_packaging_flag: true) }
|
.needs_weight_reviewed ⇒ ActiveRecord::Relation<Item>
A relation of Items that are needs weight reviewed. Active Record Scope
540
|
# File 'app/models/item.rb', line 540
scope :needs_weight_reviewed, -> { where(review_product_weight_flag: true) }
|
.non_dropships ⇒ ActiveRecord::Relation<Item>
A relation of Items that are non dropships. Active Record Scope
464
|
# File 'app/models/item.rb', line 464
scope :non_dropships, -> { where('items.dropship = false or items.dropship is null') }
|
.non_kits ⇒ ActiveRecord::Relation<Item>
A relation of Items that are non kits. Active Record Scope
429
|
# File 'app/models/item.rb', line 429
scope :non_kits, -> { where(is_kit: false) }
|
.non_publications ⇒ ActiveRecord::Relation<Item>
A relation of Items that are non publications. Active Record Scope
428
|
# File 'app/models/item.rb', line 428
scope :non_publications, -> { where.not(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_PUBLICATIONS)) }
|
.non_shipping ⇒ ActiveRecord::Relation<Item>
A relation of Items that are non shipping. Active Record Scope
.not_a_target_item ⇒ ActiveRecord::Relation<Item>
A relation of Items that are not a target item. Active Record Scope
465
|
# File 'app/models/item.rb', line 465
scope :not_a_target_item, -> { where_not_exists(:source_item_relations) }
|
.oj_programmable_tstats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are oj programmable tstats. Active Record Scope
399
|
# File 'app/models/item.rb', line 399
scope :oj_programmable_tstats, -> { oj_tstats.where(Item[:name].matches('%Programmable%')).where.not(Item[:name].matches('%Non Programmable%')) }
|
.oj_tstats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are oj tstats. Active Record Scope
398
|
# File 'app/models/item.rb', line 398
scope :oj_tstats, -> { thermostats.where(Item[:sku].matches('%4999%')) }
|
.order_by_spec ⇒ ActiveRecord::Relation<Item>
A relation of Items that are order by spec. Active Record Scope
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
|
# File 'app/models/item.rb', line 489
scope :order_by_spec, ->(spec, datatype = 'varchar', direction = 'asc') {
if %w[decimal integer numeric].include?(datatype.downcase)
order_clause = "CASE
WHEN (items.rendered_product_specifications->'#{spec}'->>'raw') ~ '^[0-9]+\\.?[0-9]*$'
THEN (items.rendered_product_specifications->'#{spec}'->>'raw')::#{datatype}
WHEN (items.rendered_product_specifications->'#{spec}'->>'raw') ~ '^[0-9]+\\.?[0-9]*\\s+[a-zA-Z]+$'
THEN (regexp_replace(items.rendered_product_specifications->'#{spec}'->>'raw', '[^0-9\\.]', '', 'g'))::#{datatype}
ELSE NULL
END #{direction}"
order(Arel.sql(order_clause))
else
order(Arel.sql("(items.rendered_product_specifications->'#{spec}'->>'raw')::#{datatype} #{direction}"))
end
}
|
.orderable_online ⇒ ActiveRecord::Relation<Item>
A relation of Items that are orderable online. Active Record Scope
438
|
# File 'app/models/item.rb', line 438
scope :orderable_online, -> { non_publications.available_to_public.active.packageable }
|
.out_of_stock ⇒ ActiveRecord::Relation<Item>
A relation of Items that are out of stock. Active Record Scope
445
446
447
|
# File 'app/models/item.rb', line 445
scope :out_of_stock, -> {
joins(:store_items).where('store_items.qty_available <= items.legacy_qty_out_of_stock ').where("store_items.location = 'AVAILABLE'")
}
|
.packageable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are packageable. Active Record Scope
370
|
# File 'app/models/item.rb', line 370
scope :packageable, -> { where(Item[:shipping_weight].gt(0)).where(Item[:base_weight].gt(0)).where(Item[:shipping_length].gt(0)).where(Item[:shipping_width].gt(0)).where(Item[:shipping_height].gt(0)) }
|
.power_modules ⇒ ActiveRecord::Relation<Item>
A relation of Items that are power modules. Active Record Scope
.powers ⇒ ActiveRecord::Relation<Item>
A relation of Items that are powers. Active Record Scope
405
|
# File 'app/models/item.rb', line 405
scope :powers, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_POWER)) }
|
.primary_product_line_slug_ltree_cont ⇒ ActiveRecord::Relation<Item>
A relation of Items that are primary product line slug ltree cont. Active Record Scope
533
534
535
536
537
538
|
# File 'app/models/item.rb', line 533
scope :primary_product_line_slug_ltree_cont, ->(term) {
sanitized = term.to_s.strip.tr(' ', '_').gsub(/[^a-zA-Z0-9_.]/, '')
return none if sanitized.blank?
where(Item[:primary_pl_path_slugs].ltree_descendant(sanitized))
}
|
.public_and_active_in_any_catalogs ⇒ ActiveRecord::Relation<Item>
A relation of Items that are public and active in any catalogs. Active Record Scope
.public_and_active_in_catalog_id ⇒ ActiveRecord::Relation<Item>
A relation of Items that are public and active in catalog id. Active Record Scope
516
517
518
519
520
|
# File 'app/models/item.rb', line 516
scope :public_and_active_in_catalog_id, ->(*catalog_ids) {
catalog_ids = [catalog_ids].flatten.filter_map(&:presence).uniq
active
.where("exists(#{CI_EXIST_CHECK_SQL} and ci.state NOT IN (?) and ci.catalog_id IN (?))", CatalogItem::HIDDEN_STATES, catalog_ids.flatten)
}
|
.publications_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are publications search. Active Record Scope
565
|
# File 'app/models/item.rb', line 565
scope :publications_search, ->(term) { canonical_search(term) }
|
.radiant_panel_controls_dual_connect ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panel controls dual connect. Active Record Scope
454
|
# File 'app/models/item.rb', line 454
scope :radiant_panel_controls_dual_connect, -> { where(sku: ItemConstants::RADIANT_PANEL_PLUG_IN_CONTROL_SKUS + ItemConstants::RADIANT_PANEL_HARDWIRE_CONTROL_SKUS) }
|
.radiant_panel_controls_hardwired ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panel controls hardwired. Active Record Scope
453
|
# File 'app/models/item.rb', line 453
scope :radiant_panel_controls_hardwired, -> { where(sku: ItemConstants::RADIANT_PANEL_HARDWIRE_CONTROL_SKUS) }
|
.radiant_panel_controls_plug_in ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panel controls plug in. Active Record Scope
451
|
# File 'app/models/item.rb', line 451
scope :radiant_panel_controls_plug_in, -> { where(sku: ItemConstants::RADIANT_PANEL_PLUG_IN_CONTROL_SKUS) }
|
.radiant_panels ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panels. Active Record Scope
.radiant_panels_hardwired ⇒ ActiveRecord::Relation<Item>
A relation of Items that are radiant panels hardwired. Active Record Scope
.ransackable_scopes(_auth_object = nil) ⇒ Object
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
|
# File 'app/models/item.rb', line 673
def self.ransackable_scopes(_auth_object = nil)
%i[keywords_search by_product_line_id pdf_search
by_product_category_id
by_primary_product_line_ids_with_descendants
active_in_catalog_ids
public_and_active_in_catalog_id
sku_and_aliases_search
tags_include
ai_search_publications
hybrid_search_publications
not_by_product_category_id
not_by_product_line_id
tags_exclude
primary_product_line_slug_ltree_cont]
end
|
.relay_panels ⇒ ActiveRecord::Relation<Item>
A relation of Items that are relay panels. Active Record Scope
.remote_services ⇒ ActiveRecord::Relation<Item>
A relation of Items that are remote services. Active Record Scope
415
|
# File 'app/models/item.rb', line 415
scope :remote_services, -> { services.where(Item[:sku].matches('%REMOTE%')) }
|
.rendered_product_specs_keys ⇒ Object
2075
2076
2077
|
# File 'app/models/item.rb', line 2075
def self.rendered_product_specs_keys
pluck('distinct jsonb_object_keys(rendered_product_specifications)').sort.map(&:to_sym)
end
|
.rendered_product_specs_options_for_select(spec_key) ⇒ Object
2079
2080
2081
2082
2083
|
# File 'app/models/item.rb', line 2079
def self.rendered_product_specs_options_for_select(spec_key)
Item.pluck("distinct rendered_product_specifications->'#{spec_key}'->>'output',rendered_product_specifications->'#{spec_key}'->>'raw'").reject do |v|
v[0].nil?
end.sort
end
|
.reservable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are reservable. Active Record Scope
483
|
# File 'app/models/item.rb', line 483
scope :reservable, -> { where(require_reservation: true) }
|
.reviewable ⇒ ActiveRecord::Relation<Item>
A relation of Items that are reviewable. Active Record Scope
.rough_in_kits ⇒ ActiveRecord::Relation<Item>
A relation of Items that are rough in kits. Active Record Scope
.sensors ⇒ ActiveRecord::Relation<Item>
A relation of Items that are sensors. Active Record Scope
406
|
# File 'app/models/item.rb', line 406
scope :sensors, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_SENSORS)) }
|
.services ⇒ ActiveRecord::Relation<Item>
A relation of Items that are services. Active Record Scope
414
|
# File 'app/models/item.rb', line 414
scope :services, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_SERVICES)) }
|
.shipping ⇒ ActiveRecord::Relation<Item>
A relation of Items that are shipping. Active Record Scope
462
|
# File 'app/models/item.rb', line 462
scope :shipping, -> { where(tax_class: 'shp') }
|
.sku_aliases_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are sku aliases search. Active Record Scope
443
|
# File 'app/models/item.rb', line 443
scope :sku_aliases_search, ->(sku) { where('items.sku_aliases @> ARRAY[:sku]::varchar[]', sku:) }
|
.sku_and_aliases_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are sku and aliases search. Active Record Scope
444
|
# File 'app/models/item.rb', line 444
scope :sku_and_aliases_search, ->(sku) { where('items.sku ILIKE :sku OR items.sku_aliases @> ARRAY[:sku]::varchar[]', sku:) }
|
.sku_is_membrane?(sku) ⇒ Boolean
1401
1402
1403
|
# File 'app/models/item.rb', line 1401
def self.sku_is_membrane?(sku)
ItemConstants::MEMBRANE_SKUS.include?(sku)
end
|
.sku_search ⇒ ActiveRecord::Relation<Item>
A relation of Items that are sku search. Active Record Scope
557
|
# File 'app/models/item.rb', line 557
scope :sku_search, ->(sku) { canonical_search(sku) }
|
.smart_services ⇒ ActiveRecord::Relation<Item>
A relation of Items that are smart services. Active Record Scope
508
|
# File 'app/models/item.rb', line 508
scope :smart_services, -> { where(Item[:primary_pl_path_slugs].ltree_descendant(LtreePaths::PL_SERVICES)) }
|
.smartpresets ⇒ ActiveRecord::Relation<Item>
A relation of Items that are smartpresets. Active Record Scope
.smartstats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are smartstats. Active Record Scope
.snow_melting_elements ⇒ ActiveRecord::Relation<Item>
A relation of Items that are snow melting elements. Active Record Scope
408
|
# File 'app/models/item.rb', line 408
scope :snow_melting_elements, -> { heating_elements.where(Item[:primary_pl_path_slugs].ltree_descendant(LtreePaths::PL_SNOW_MELTING)) }
|
.spare_parts ⇒ ActiveRecord::Relation<Item>
A relation of Items that are spare parts. Active Record Scope
417
|
# File 'app/models/item.rb', line 417
scope :spare_parts, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_SPARE_PARTS)) }
|
.tax_classes_for_select ⇒ Object
917
918
919
|
# File 'app/models/item.rb', line 917
def self.tax_classes_for_select
TAX_CLASSES.invert
end
|
.tempzones ⇒ ActiveRecord::Relation<Item>
A relation of Items that are tempzones. Active Record Scope
.thermostats ⇒ ActiveRecord::Relation<Item>
A relation of Items that are thermostats. Active Record Scope
A relation of Items that are tools. Active Record Scope
412
|
# File 'app/models/item.rb', line 412
scope :tools, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_TOOLS)) }
|
.towel_warmer_controls_dual_connect ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmer controls dual connect. Active Record Scope
396
|
# File 'app/models/item.rb', line 396
scope :towel_warmer_controls_dual_connect, -> { where(sku: ItemConstants::TOWEL_WARMER_PLUG_IN_CONTROL_SKUS + ItemConstants::TOWEL_WARMER_HARDWIRE_CONTROL_SKUS) }
|
.towel_warmer_controls_hardwired ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmer controls hardwired. Active Record Scope
393
|
# File 'app/models/item.rb', line 393
scope :towel_warmer_controls_hardwired, -> { where(sku: ItemConstants::TOWEL_WARMER_HARDWIRE_CONTROL_SKUS) }
|
.towel_warmer_controls_plug_in ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmer controls plug in. Active Record Scope
392
|
# File 'app/models/item.rb', line 392
scope :towel_warmer_controls_plug_in, -> { where(sku: ItemConstants::TOWEL_WARMER_PLUG_IN_CONTROL_SKUS) }
|
.towel_warmers ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmers. Active Record Scope
.towel_warmers_hardwired ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmers hardwired. Active Record Scope
.towel_warmers_plug_in ⇒ ActiveRecord::Relation<Item>
A relation of Items that are towel warmers plug in. Active Record Scope
.underlayments ⇒ ActiveRecord::Relation<Item>
A relation of Items that are underlayments. Active Record Scope
.uom_select_options ⇒ Object
913
914
915
|
# File 'app/models/item.rb', line 913
def self.uom_select_options
%w[each feet]
end
|
.upgrades ⇒ ActiveRecord::Relation<Item>
A relation of Items that are upgrades. Active Record Scope
410
|
# File 'app/models/item.rb', line 410
scope :upgrades, -> { where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_UPGRADES)) }
|
.with_all_catalog_items_discontinued ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with all catalog items discontinued. Active Record Scope
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
|
# File 'app/models/item.rb', line 608
scope :with_all_catalog_items_discontinued, -> {
where(%{
(
SELECT COUNT(*)
FROM catalog_items ci1
INNER JOIN store_items si1 ON ci1.store_item_id = si1.id
WHERE si1.item_id = items.id
) > 0
AND
(
SELECT COUNT(*)
FROM catalog_items ci1
INNER JOIN store_items si1 ON ci1.store_item_id = si1.id
WHERE si1.item_id = items.id
) =
(
SELECT COUNT(*)
FROM catalog_items ci2
INNER JOIN store_items si2 ON ci2.store_item_id = si2.id
WHERE si2.item_id = items.id AND ci2.state = 'discontinued'
)
})
}
|
.with_features ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with features. Active Record Scope
507
|
# File 'app/models/item.rb', line 507
scope :with_features, -> { where.not(feature_1: nil, feature_2: nil, feature_3: nil, feature_4: nil, feature_5: nil) }
|
.with_product_specification ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with product specification. Active Record Scope
467
468
469
470
471
472
473
474
475
476
|
# File 'app/models/item.rb', line 467
scope :with_product_specification, ->(token, value, grouping = nil, include_absence_of = false) {
match_hsh = { raw: TypeCoercer.coerce(value) }
grouping = grouping.to_s.titleize if grouping.is_a?(Symbol)
match_hsh[:grouping] = grouping if grouping.present?
test_condition_criteria = +'items.rendered_product_specifications @> :condition_1'
test_condition_criteria << ' OR NOT (rendered_product_specifications ? :condition_2)' if include_absence_of
condition_1 = { token.to_s => match_hsh }.to_json
condition_2 = token.to_s
where(test_condition_criteria, condition_1:, condition_2:)
}
|
.with_stock ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with stock. Active Record Scope
421
422
423
|
# File 'app/models/item.rb', line 421
scope :with_stock, -> {
where("exists(select id from store_items where store_items.item_id = items.id and qty_on_hand > 0 and store_items.location = 'AVAILABLE')")
}
|
.with_stock_for ⇒ ActiveRecord::Relation<Item>
A relation of Items that are with stock for. Active Record Scope
424
425
426
|
# File 'app/models/item.rb', line 424
scope :with_stock_for, ->(store_id) {
where("exists(select id from store_items where store_items.store_id = ? and store_items.item_id = items.id and qty_on_hand > 0 and store_items.location = 'AVAILABLE')", store_id)
}
|
Instance Method Details
#all_images ⇒ Object
Find all images linked to this item directly or by combination of product line or category ancestry
This is a simpler method than image retriever
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
|
# File 'app/models/item.rb', line 2384
def all_images
query = Image.left_outer_joins(:items)
.left_outer_joins(:product_lines)
.left_outer_joins(:product_categories)
conditions = []
plids = product_line_ancestry_ids
pcids = product_category_ancestry_ids
conditions << Item[:id].eq(id)
conditions << ProductLine[:id].in(plids).and(ProductCategory[:id].eq(nil)) if plids.present?
conditions << ProductLine[:id].eq(nil).and(ProductCategory[:id].in(pcids)) if pcids.present?
conditions << ProductLine[:id].in(plids).and(ProductCategory[:id].in(pcids)) if plids.present? && pcids.present?
combined_conditions = conditions.reduce { |acc, condition| acc.or(condition) }
query.where(combined_conditions)
end
|
#all_my_publications(categories: nil, locale: nil, publication_category_paths: nil) ⇒ Object
1725
1726
1727
|
# File 'app/models/item.rb', line 1725
def all_my_publications(categories: nil, locale: nil, publication_category_paths: nil)
Item::PublicationRetriever.new.process(self, categories:, locale:, publication_category_paths:).all_publications
end
|
#all_site_maps ⇒ Object
1940
1941
1942
|
# File 'app/models/item.rb', line 1940
def all_site_maps
[] + site_maps.to_a + catalog_item_site_maps.to_a
end
|
#all_uploads ⇒ Object
2442
2443
2444
|
# File 'app/models/item.rb', line 2442
def all_uploads
uploads.order(Upload[:created_at].desc)
end
|
#any_box_changed? ⇒ Boolean
2438
2439
2440
|
# File 'app/models/item.rb', line 2438
def any_box_changed?
box1_changed? || box2_changed? || box3_changed?
end
|
#articles ⇒ ActiveRecord::Relation<Article>
299
|
# File 'app/models/item.rb', line 299
has_and_belongs_to_many :articles
|
#assortment_instructions ⇒ ActiveRecord::Relation<AssortmentInstruction>
302
|
# File 'app/models/item.rb', line 302
has_and_belongs_to_many :assortment_instructions, inverse_of: :items
|
#async_update_rendered_product_specifications ⇒ Object
1936
1937
1938
|
# File 'app/models/item.rb', line 1936
def async_update_rendered_product_specifications
ItemAttributeWorker.perform_async(id)
end
|
#authenticated_url(dimensions = nil) ⇒ Object
854
855
856
|
# File 'app/models/item.rb', line 854
def authenticated_url(dimensions = nil)
(dimensions.nil? ? literature.attachment.url : literature.attachment.thumb(dimensions, format: 'png', frame: '0').url) if literature
end
|
#auto_translate(reset_previous_values: false, locales: nil) ⇒ Object
1793
1794
1795
1796
1797
1798
1799
1800
1801
|
# File 'app/models/item.rb', line 1793
def auto_translate(reset_previous_values: false, locales: nil)
locales ||= content_locales_to_render
Rails.logger.info "Product Specifications for Item #{id}:#{sku} will be rendered in #{locales.join(', ')}"
auto_translate_product_specifications(reset_previous_values:, locales:)
reload
update_rendered_product_specifications(locales:)
:success
end
|
#auto_translate_attributes(reset_previous_values: false, locales: nil) ⇒ Object
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
|
# File 'app/models/item.rb', line 1803
def auto_translate_attributes(reset_previous_values: false, locales: nil)
locales ||= content_locales_to_render
res = {}
TRANSLATABLE_ATTRIBUTES.each do |attr_name|
next unless (english_val = send(:"#{attr_name}_en").presence)
res[attr_name] = {}
locales.each do |locale|
next if !reset_previous_values && Mobility.with_locale(locale) { send(attr_name.to_sym) }.present?
localized_val = TranslationKey.translate(english_val, locale, namespace: TRANSLATION_NAMESPACE, auto_create: false, auto_translate: true, resource: self, resource_attribute: attr_name)
next if localized_val.blank?
Mobility.with_locale(locale) { send(:"#{attr_name}=", localized_val, locale:) }
res[attr_name][locale] = localized_val
end
end
if save && res
:success
else
:error
end
end
|
#auto_translate_product_specifications(reset_previous_values: false, locales: nil) ⇒ Object
1832
1833
1834
1835
1836
1837
1838
|
# File 'app/models/item.rb', line 1832
def auto_translate_product_specifications(reset_previous_values: false, locales: nil)
locales ||= content_locales_to_render
product_specifications.each do |ps|
ps.auto_translate(reset_previous_values:, locales:)
end
:success
end
|
#available_content_locales ⇒ Object
Returns all locales from the catalog this item is part of
1973
1974
1975
1976
|
# File 'app/models/item.rb', line 1973
def available_content_locales
Catalog.eager_load(:catalog_items).merge(catalog_items.not_discontinued).pluck(:locales).flatten.compact.uniq.sort
end
|
#available_token_options_from_product_specifications ⇒ Object
2119
2120
2121
|
# File 'app/models/item.rb', line 2119
def available_token_options_from_product_specifications
(rendered_product_specifications || {}).each_with_object({}.with_indifferent_access) { |(rpk, rpv), hsh| hsh[rpk] = rpv[:output] }
end
|
#base_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
1595
1596
1597
|
# File 'app/models/item.rb', line 1595
def base_weight_converted(unit: 'lbs', precision: nil)
express_weight_converted base_weight, unit:, precision:
end
|
#box1_changed? ⇒ Boolean
2418
2419
2420
|
# File 'app/models/item.rb', line 2418
def box1_changed?
%i[shipping_weight shipping_length shipping_width shipping_weight].any? { |fn| send(:"#{fn}_previously_changed?") || send(:"will_save_change_to_#{fn}?") }
end
|
#box1_defined? ⇒ Boolean
2414
2415
2416
|
# File 'app/models/item.rb', line 2414
def box1_defined?
%i[shipping_weight shipping_length shipping_width shipping_weight].all? { |fn| send(fn)&.positive? }
end
|
#box2_changed? ⇒ Boolean
2426
2427
2428
|
# File 'app/models/item.rb', line 2426
def box2_changed?
%i[box2_shipping_weight box2_shipping_length box2_shipping_width box2_shipping_weight].any? { |fn| send(:"#{fn}_previously_changed?") || send(:"will_save_change_to_#{fn}?") }
end
|
#box2_defined? ⇒ Boolean
2422
2423
2424
|
# File 'app/models/item.rb', line 2422
def box2_defined?
%i[box2_shipping_weight box2_shipping_length box2_shipping_width box2_shipping_height].all? { |fn| send(fn)&.positive? }
end
|
#box2_shipping_dimensions_to_package ⇒ Object
1626
1627
1628
1629
1630
1631
|
# File 'app/models/item.rb', line 1626
def box2_shipping_dimensions_to_package
return unless box2_defined?
Shipping::Container.new(length: box2_shipping_length, width: box2_shipping_width, height: box2_shipping_height, weight: box2_shipping_weight,
container_type:)
end
|
#box2_shipping_height_converted(unit: 'in', precision: 0) ⇒ Object
1563
1564
1565
1566
1567
|
# File 'app/models/item.rb', line 1563
def box2_shipping_height_converted(unit: 'in', precision: 0)
return unless box2_shipping_height
RubyUnits::Unit.new("#{box2_shipping_height} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#box2_shipping_length_converted(unit: 'in', precision: 0) ⇒ Object
1551
1552
1553
1554
1555
|
# File 'app/models/item.rb', line 1551
def box2_shipping_length_converted(unit: 'in', precision: 0)
return unless box2_shipping_length
RubyUnits::Unit.new("#{box2_shipping_length} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#box2_shipping_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
1611
1612
1613
|
# File 'app/models/item.rb', line 1611
def box2_shipping_weight_converted(unit: 'lbs', precision: nil)
express_weight_converted box2_shipping_weight, unit:, precision:
end
|
#box2_shipping_width_converted(unit: 'in', precision: 0) ⇒ Object
1557
1558
1559
1560
1561
|
# File 'app/models/item.rb', line 1557
def box2_shipping_width_converted(unit: 'in', precision: 0)
return unless box2_shipping_width
RubyUnits::Unit.new("#{box2_shipping_width} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#box3_changed? ⇒ Boolean
2434
2435
2436
|
# File 'app/models/item.rb', line 2434
def box3_changed?
%i[box3_shipping_weight box3_shipping_length box3_shipping_width box3_shipping_weight].any? { |fn| send(:"#{fn}_previously_changed?") || send(:"will_save_change_to_#{fn}?") }
end
|
#box3_defined? ⇒ Boolean
2430
2431
2432
|
# File 'app/models/item.rb', line 2430
def box3_defined?
%i[box3_shipping_weight box3_shipping_length box3_shipping_width box3_shipping_height].all? { |fn| send(fn)&.positive? }
end
|
#box3_shipping_dimensions_to_package ⇒ Object
1633
1634
1635
1636
1637
1638
|
# File 'app/models/item.rb', line 1633
def box3_shipping_dimensions_to_package
return unless box3_defined?
Shipping::Container.new(length: box3_shipping_length, width: box3_shipping_width, height: box3_shipping_height, weight: box3_shipping_weight,
container_type:)
end
|
#box3_shipping_height_converted(unit: 'in', precision: 0) ⇒ Object
1581
1582
1583
1584
1585
|
# File 'app/models/item.rb', line 1581
def box3_shipping_height_converted(unit: 'in', precision: 0)
return unless box3_shipping_height
RubyUnits::Unit.new("#{box3_shipping_height} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#box3_shipping_length_converted(unit: 'in', precision: 0) ⇒ Object
1569
1570
1571
1572
1573
|
# File 'app/models/item.rb', line 1569
def box3_shipping_length_converted(unit: 'in', precision: 0)
return unless box3_shipping_length
RubyUnits::Unit.new("#{box3_shipping_length} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#box3_shipping_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
1615
1616
1617
|
# File 'app/models/item.rb', line 1615
def box3_shipping_weight_converted(unit: 'lbs', precision: nil)
express_weight_converted box3_shipping_weight, unit:, precision:
end
|
#box3_shipping_width_converted(unit: 'in', precision: 0) ⇒ Object
1575
1576
1577
1578
1579
|
# File 'app/models/item.rb', line 1575
def box3_shipping_width_converted(unit: 'in', precision: 0)
return unless box3_shipping_width
RubyUnits::Unit.new("#{box3_shipping_width} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#boxes ⇒ Object
993
994
995
996
997
998
|
# File 'app/models/item.rb', line 993
def boxes
b = [[shipping_length, shipping_width, shipping_height, shipping_weight]]
b << [box2_shipping_length, box2_shipping_width, box2_shipping_height, box2_shipping_weight] if box2_defined?
b << [box3_shipping_length, box3_shipping_width, box3_shipping_height, box3_shipping_weight] if box3_defined?
b
end
|
#boxes_shipping_dimensions_and_weights_converted(units: { length: 'in', weight: 'lbs' }, precision: 0) ⇒ Object
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
|
# File 'app/models/item.rb', line 1073
def boxes_shipping_dimensions_and_weights_converted(units: { length: 'in', weight: 'lbs' }, precision: 0)
boxes.map do |b|
[
RubyUnits::Unit.new("#{b[0]} in").convert_to(units[:length].to_s).scalar.to_f.round(precision),
RubyUnits::Unit.new("#{b[1]} in").convert_to(units[:length].to_s).scalar.to_f.round(precision),
RubyUnits::Unit.new("#{b[2]} in").convert_to(units[:length].to_s).scalar.to_f.round(precision),
RubyUnits::Unit.new("#{b[3]} lb").convert_to(units[:weight].to_s).scalar.to_f.round(precision)
]
end
end
|
#boxes_shipping_dimensions_and_weights_display(units: { length: 'in', weight: 'lbs' }, precision: 0) ⇒ Object
1084
1085
1086
|
# File 'app/models/item.rb', line 1084
def boxes_shipping_dimensions_and_weights_display(units: { length: 'in', weight: 'lbs' }, precision: 0)
boxes_shipping_dimensions_and_weights_converted(units:, precision:).map { |b| "#{b[0]} #{units[:length]} x #{b[1]} #{units[:length]} x #{b[2]} #{units[:length]} x #{b[3]} #{units[:weight]}" }
end
|
#boxes_to_packages ⇒ Object
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
|
# File 'app/models/item.rb', line 1000
def boxes_to_packages
packages = []
boxes.each do |box|
le, wi, he, we, = box + [0]
package = Shipping::Container.new(length: le, width: wi, height: he, weight: we, container_type: Shipment.container_types.invert[0])
package.add_content(id, 1)
packages << package
end
packages
end
|
256
|
# File 'app/models/item.rb', line 256
belongs_to :brand, optional: true
|
#calculate_ideal_cable_spacing ⇒ Object
1419
1420
1421
1422
1423
1424
1425
|
# File 'app/models/item.rb', line 1419
def calculate_ideal_cable_spacing
res = nil
if primary_product_line && primary_product_line.get_first_heating_system_type.present?
res = HeatingElementProductLineOption.get_field_array_from_options(:ideal_cable_spacing, product_line_id: primary_product_line.get_first_heating_system_type.id).first
end
res
end
|
#calculate_is_cable_system? ⇒ Boolean
1410
1411
1412
1413
1414
1415
1416
1417
|
# File 'app/models/item.rb', line 1410
def calculate_is_cable_system?
res = nil
if primary_product_line && primary_product_line.get_first_heating_system_type.present? && HeatingElementProductLineOption.get_field_array_from_options(:require_cable_spacing,
product_line_id: primary_product_line.get_first_heating_system_type.id).first
res = true
end
res
end
|
#calculate_item_fields ⇒ Object
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
|
# File 'app/models/item.rb', line 975
def calculate_item_fields
sorted_dims = [shipping_length, shipping_width, shipping_height].sort_by(&:to_f)
self.shipping_length = sorted_dims[2]
self.shipping_width = sorted_dims[1]
self.shipping_height = sorted_dims[0]
box2_sorted_dims = [box2_shipping_length, box2_shipping_width, box2_shipping_height].sort_by(&:to_f)
self.box2_shipping_length = box2_sorted_dims[2]
self.box2_shipping_width = box2_sorted_dims[1]
self.box2_shipping_height = box2_sorted_dims[0]
box3_sorted_dims = [box3_shipping_length, box3_shipping_width, box3_shipping_height].sort_by(&:to_f)
self.box3_shipping_length = box3_sorted_dims[2]
self.box3_shipping_width = box3_sorted_dims[1]
self.box3_shipping_height = box3_sorted_dims[0]
self.ideal_cable_spacing = (calculate_ideal_cable_spacing if (self.is_cable_system = calculate_is_cable_system?))
true
end
|
#can_be_packaged? ⇒ Boolean
966
967
968
|
# File 'app/models/item.rb', line 966
def can_be_packaged?
Item.packageable.where(id:).present?
end
|
#canonical_path ⇒ Object
Hierarchical canonical path via product line ancestry + SKU.
e.g. "towel-warmer/barcelona/TW-BC-08BS-FS2"
720
721
722
723
724
725
726
727
|
# File 'app/models/item.rb', line 720
def canonical_path
return nil unless primary_product_line
pl_path = primary_product_line.canonical_path
return nil if pl_path.blank?
"#{pl_path}/#{sku}"
end
|
#canonical_sku ⇒ Object
1680
1681
1682
1683
1684
1685
1686
1687
|
# File 'app/models/item.rb', line 1680
def canonical_sku
sku_match = sku.match(/(.*)-([[:alpha:]])$/)
if sku_match && (sku_match.length == 3)
sku_match[1]
else
sku
end
end
|
#canonical_url(locale: I18n.locale) ⇒ Object
Canonical URL with locale prefix.
e.g. "/en-US/towel-warmer/barcelona/TW-BC-08BS-FS2"
731
732
733
734
735
736
|
# File 'app/models/item.rb', line 731
def canonical_url(locale: I18n.locale)
path = canonical_path
return nil unless path
"/#{locale}/#{path}"
end
|
#catalog_item_site_maps ⇒ ActiveRecord::Relation<SiteMap>
285
|
# File 'app/models/item.rb', line 285
has_many :catalog_item_site_maps, class_name: 'SiteMap', source: :site_maps, through: :catalog_items
|
#catalog_items ⇒ ActiveRecord::Relation<CatalogItem>
263
|
# File 'app/models/item.rb', line 263
has_many :catalog_items, through: :store_items, inverse_of: :item
|
#catalogs ⇒ ActiveRecord::Relation<Catalog>
264
|
# File 'app/models/item.rb', line 264
has_many :catalogs, through: :catalog_items
|
#check_oversize?(carrier: nil) ⇒ Boolean
Check if item is oversize, optionally for a specific carrier
When no carrier specified, checks all carriers (item is oversize if ANY carrier flags it)
1666
1667
1668
1669
1670
1671
1672
|
# File 'app/models/item.rb', line 1666
def check_oversize?(carrier: nil)
r = run_shipping_audit(carrier:)
r.any?(&:oversize_conditions?)
rescue StandardError
logger.error "Oversize check failed on item id #{id}, returning false"
false
end
|
#compatible_items ⇒ ActiveRecord::Relation<CompatibleItem>
290
|
# File 'app/models/item.rb', line 290
has_many :compatible_items, -> { merge(ItemRelation.replacement_parts) }, through: :source_item_relations, source: :source_item
|
#container_type ⇒ Object
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
|
# File 'app/models/item.rb', line 2334
def container_type
if ships_via_crate?
Shipment.container_types.keys[2] elsif shipping_class == 'freight_service'
Shipment.container_types.keys[1] else
Shipment.container_types.keys.first end
end
|
#content_locales_to_render ⇒ Object
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
|
# File 'app/models/item.rb', line 1978
def content_locales_to_render
if is_publication?
Mobility.available_locales
else
locales = (Mobility.available_locales & ([I18n.default_locale] + available_content_locales)).uniq
locales.delete(:'en-US')
locales.delete(:'en-CA')
locales.delete(:'fr-CA') if locales.include?(:fr)
locales
end
end
|
#controllable? ⇒ Boolean
761
762
763
764
765
|
# File 'app/models/item.rb', line 761
def controllable?
is_heating_element? ||
is_towel_warmer? ||
is_radiant_panel?
end
|
#country_of_origin_iso3 ⇒ Object
705
706
707
708
709
|
# File 'app/models/item.rb', line 705
def country_of_origin_iso3
return unless coo
ISO3166::Country[coo]&.alpha3
end
|
#country_of_origin_name ⇒ Object
699
700
701
702
703
|
# File 'app/models/item.rb', line 699
def country_of_origin_name
return unless coo
ISO3166::Country[coo]&.iso_short_name
end
|
#create_template_specifications ⇒ Object
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
|
# File 'app/models/item.rb', line 2085
def create_template_specifications
return OpenStruct.new(created: [], errors: ['Missing Product Line or Product Category']) unless primary_product_line_id || product_category_id
res = Item::SpecificationsRetriever.new.process(self, template: true)
res_classic = Item::SpecificationsRetriever.new.process(self, template: false)
ps_created = []
ps_errors = []
res.specifications.each do |template_spec|
next if spec_value(template_spec.token).present? || res_classic.specifications.any? { |s| s.token == template_spec.token }
s = template_spec.dup
s.product_category_id = nil
s.product_line_id = nil
s.template = false
s.item_ids = [id].compact
s.template_product_specification_id = template_spec.id
if s.save
ps_created << s
else
ps_errors << "Template Spec id #{template_spec.id} : #{template_spec} could not be created for item #{id}|#{sku}. Errors: #{s.errors_to_s}"
end
end
OpenStruct.new(created: ps_created, errors: ps_errors)
end
|
#current_supplier_cost ⇒ Object
755
756
757
758
759
|
# File 'app/models/item.rb', line 755
def current_supplier_cost
return 'not found' if supplier_items.empty? || (current_price = supplier_items.order(created_at: :desc).first&.current_price).nil?
"#{current_price.unit_cost} #{current_price.currency}"
end
|
#deep_dup ⇒ Object
219
220
221
222
223
224
225
226
|
# File 'app/models/item.rb', line 219
def deep_dup
deep_clone(
include: :item_product_lines,
except: %i[upc upload_id last_sale_date first_sale_date popularity amazon_asin]
) do |_original, copy|
copy.popularity = nil if copy.is_a?(Item)
end
end
|
#default_short_description ⇒ Object
1748
1749
1750
1751
1752
|
# File 'app/models/item.rb', line 1748
def default_short_description
return unless (dd = detailed_description.to_s.squish.presence) && dd.present?
dd.truncate(120, separator: ' ')
end
|
#detailed_description ⇒ Object
Creates a text description from the html rendered description
1744
1745
1746
|
# File 'app/models/item.rb', line 1744
def detailed_description
html_to_text(rendered_detailed_description_html)
end
|
#digital_assets ⇒ ActiveRecord::Relation<DigitalAsset>
300
|
# File 'app/models/item.rb', line 300
has_and_belongs_to_many :digital_assets
|
#direct_packings ⇒ ActiveRecord::Relation<Packing>
293
|
# File 'app/models/item.rb', line 293
has_many :direct_packings, class_name: 'Packing', inverse_of: :item, dependent: :destroy
|
#direct_product_specifications ⇒ ActiveRecord::Relation<ProductSpecification>
304
|
# File 'app/models/item.rb', line 304
has_and_belongs_to_many :direct_product_specifications, class_name: 'ProductSpecification', dependent: :destroy, inverse_of: :items
|
#discontinue_self_and_dependents ⇒ Object
956
957
958
959
960
961
962
963
964
|
# File 'app/models/item.rb', line 956
def discontinue_self_and_dependents
self.class.transaction do
store_items.each do |si|
si.catalog_items.each(&:discontinue) si.update_attribute(:is_discontinued, true) end
update!(is_discontinued: true, cycle_count_grouping: 'not_set')
end
end
|
#discontinue_store_and_catalog_items ⇒ Object
949
950
951
952
953
954
|
# File 'app/models/item.rb', line 949
def discontinue_store_and_catalog_items
return if is_publication?
discontinue_self_and_dependents if is_discontinued?
true
end
|
#ean13 ⇒ Object
843
844
845
846
847
848
849
850
851
852
|
# File 'app/models/item.rb', line 843
def ean13
return if upc.blank?
case upc.size
when 12
"0#{upc}"
when 13
upc
end
end
|
#edi_communication_logs ⇒ ActiveRecord::Relation<EdiCommunicationLog>
295
|
# File 'app/models/item.rb', line 295
has_many :edi_communication_logs, through: :edi_documents
|
#edi_documents ⇒ ActiveRecord::Relation<EdiDocument>
294
|
# File 'app/models/item.rb', line 294
has_many :edi_documents, dependent: :destroy
|
#effective_nmfc_class ⇒ Object
Alias for Product_category#effective_nmfc_class
640
641
642
643
644
645
646
647
648
649
|
# File 'app/models/item.rb', line 640
delegate :is_control?,
:is_upgrade?,
:is_accessory?,
:is_relay_panel?,
:is_custom_mat?,
:effective_nmfc_code,
:effective_nmfc_class,
:is_publication?,
to: :product_category,
allow_nil: true
|
#effective_nmfc_code ⇒ Object
Alias for Product_category#effective_nmfc_code
640
641
642
643
644
645
646
647
648
649
|
# File 'app/models/item.rb', line 640
delegate :is_control?,
:is_upgrade?,
:is_accessory?,
:is_relay_panel?,
:is_custom_mat?,
:effective_nmfc_code,
:effective_nmfc_class,
:is_publication?,
to: :product_category,
allow_nil: true
|
#effective_option_name ⇒ Object
2275
2276
2277
|
# File 'app/models/item.rb', line 2275
def effective_option_name
option_name.presence || public_short_name.presence || name
end
|
#effective_seo_description(char_limit: nil) ⇒ Object
858
859
860
861
862
863
864
865
866
867
868
869
870
|
# File 'app/models/item.rb', line 858
def effective_seo_description(char_limit: nil)
char_limit ||= SEO_META_DESCRIPTION_MAX_LENGTH
d = seo_description.presence || short_description.presence || public_description_text || name
return unless d
d.tr!("\n", ' ')
d.tr!("\r", ' ')
d.tr!("\t", ' ')
d.squeeze!(' ')
d = d.truncate(char_limit, separator: /\s/) if char_limit
d.squish
end
|
#effective_seo_keywords ⇒ Object
1775
1776
1777
1778
1779
1780
1781
1782
|
# File 'app/models/item.rb', line 1775
def effective_seo_keywords
return seo_keywords.presence if seo_keywords.present?
pl_keywords = primary_product_line&.effective_seo_keywords
return pl_keywords.join(',') if pl_keywords.is_a?(Array)
pl_keywords.presence
end
|
#effective_seo_title(skip_sku: false) ⇒ Object
872
873
874
875
876
877
878
879
880
881
882
883
|
# File 'app/models/item.rb', line 872
def effective_seo_title(skip_sku: false)
s = (seo_title.presence || public_short_name.presence || public_name.presence).to_s
if !skip_sku && (s.size + sku.size + 3) < SEO_TITLE_MAX_LENGTH && s.exclude?(sku)
s << " (#{sku})"
end
if s.size > SEO_TITLE_MAX_LENGTH
s.truncate(SEO_TITLE_MAX_LENGTH, separator: /\s/, omission: '')
end
s
end
|
247
|
# File 'app/models/item.rb', line 247
belongs_to :exclusive_item_group, inverse_of: :items, optional: true
|
#express_weight_converted(weight_in_lbs, unit: 'lbs', precision: nil) ⇒ Object
1587
1588
1589
1590
1591
1592
1593
|
# File 'app/models/item.rb', line 1587
def express_weight_converted(weight_in_lbs, unit: 'lbs', precision: nil)
return unless weight_in_lbs
precision ||= (unit == 'g' ? 0 : 2)
d = RubyUnits::Unit.new("#{weight_in_lbs} lbs")
d.convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#facet ⇒ Object
2256
2257
2258
2259
2260
2261
2262
2263
2264
|
# File 'app/models/item.rb', line 2256
def facet
return unless (pl = primary_product_line&.get_first_show_in_sales_portal_ancestor(exclude_main_line: false))
return unless (pc = product_category)
potential_facets = pl.facets.select { |f| f.product_category_ids.to_a.include?(pc.id) }
return if potential_facets.blank?
potential_facets.first
end
|
#facet_sort_keys ⇒ Object
2271
2272
2273
|
# File 'app/models/item.rb', line 2271
def facet_sort_keys
facet&.sort_keys
end
|
#facet_tokens ⇒ Object
2267
2268
2269
|
# File 'app/models/item.rb', line 2267
def facet_tokens
facet&.grouping_spec_tokens&.reject(&:blank?)
end
|
#features ⇒ Object
Features are a sub category of specs with feature priority set
1896
1897
1898
1899
1900
1901
1902
|
# File 'app/models/item.rb', line 1896
def features
if has_features?
[feature_1, feature_2, feature_3, feature_4, feature_5].filter_map(&:presence)
else
inherited_features end
end
|
#fix_name ⇒ Object
1754
1755
1756
1757
1758
1759
|
# File 'app/models/item.rb', line 1754
def fix_name
self.name = name.tr("'", '′')
self.name = name.tr('"', '″')
end
|
#fixture_key ⇒ Object
1427
1428
1429
|
# File 'app/models/item.rb', line 1427
def fixture_key
sku.to_s
end
|
#freight_class ⇒ Object
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
|
# File 'app/models/item.rb', line 1054
def freight_class
freight_class = nil
if base_weight.present? && shipping_volume_in_cubic_feet.present?
pcf = base_weight / shipping_volume_in_cubic_feet
Shipping::UpsFreight::FREIGHT_CLASS_BY_PCF.each do |fc, pcf_limits|
if pcf >= pcf_limits[:lower] && pcf < pcf_limits[:upper]
freight_class = fc.to_i
break
end
end
end
freight_class
end
|
#generate_refurbished_sku ⇒ Object
1705
1706
1707
|
# File 'app/models/item.rb', line 1705
def generate_refurbished_sku
"#{sku}-BTK"
end
|
#get_quantity_from_sqft(input_sqft, add_one_extra = false) ⇒ Object
1459
1460
1461
1462
1463
|
# File 'app/models/item.rb', line 1459
def get_quantity_from_sqft(input_sqft, = false)
qty = ? 1 : 0
qty += (input_sqft / sqft).ceil.to_i if sqft.to_f > 0.0
qty
end
|
#gross_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
1599
1600
1601
|
# File 'app/models/item.rb', line 1599
def gross_weight_converted(unit: 'lbs', precision: nil)
express_weight_converted gross_weight, unit:, precision:
end
|
#gtin13 ⇒ Object
1501
1502
1503
|
# File 'app/models/item.rb', line 1501
def gtin13
"0#{upc}"
end
|
#gtin_type ⇒ Object
738
739
740
741
742
|
# File 'app/models/item.rb', line 738
def gtin_type
return :missing if upc.blank?
Item::UpcBarcode.identify_type(upc)
end
|
#gtin_type_friendly ⇒ Object
744
745
746
747
748
749
750
751
752
753
|
# File 'app/models/item.rb', line 744
def gtin_type_friendly
case gtin_type
when :gtin12
'GTIN-12/UPC'
when :gtin13
'GTIN-13/EAN'
else
'Unknown/Invalid'
end
end
|
#harmonization_code_hs6(with_dot = true) ⇒ Object
Returns the first 6 digits of the harmonization code (HS6)
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
|
# File 'app/models/item.rb', line 1510
def harmonization_code_hs6(with_dot = true)
return nil if harmonization_code.blank?
digits = harmonization_code.delete('.').first(6)
if with_dot
"#{digits[0..3]}.#{digits[4..5]}"
else
digits
end
end
|
#has_ean13? ⇒ Boolean
839
840
841
|
# File 'app/models/item.rb', line 839
def has_ean13?
upc && upc.size == 13
end
|
#has_features? ⇒ Boolean
1891
1892
1893
|
# File 'app/models/item.rb', line 1891
def has_features?
[feature_1, feature_2, feature_3, feature_4, feature_5].any?(&:present?)
end
|
#has_heating_system? ⇒ Boolean
1443
1444
1445
|
# File 'app/models/item.rb', line 1443
def has_heating_system?
primary_product_line.get_first_heating_system_type.present?
end
|
#has_stock? ⇒ Boolean
935
936
937
|
# File 'app/models/item.rb', line 935
def has_stock?
self.class.with_stock.exists?(id:)
end
|
#has_stock_in_store?(st_id) ⇒ Boolean
939
940
941
|
# File 'app/models/item.rb', line 939
def has_stock_in_store?(st_id)
self.class.with_stock_for(st_id).exists?(id:)
end
|
#image_profiles ⇒ ActiveRecord::Relation<ImageProfile>
296
|
# File 'app/models/item.rb', line 296
has_many :image_profiles, -> { image_order }
|
#image_url(dimensions, options = {}) ⇒ Object
1110
1111
1112
1113
|
# File 'app/models/item.rb', line 1110
def image_url(dimensions, options = {})
opts = { size: dimensions }.merge(options)
image_url2(opts)
end
|
#image_url2(options = {}) ⇒ Object
Use this new form that passes the options straight to the url generator
1102
1103
1104
1105
1106
1107
1108
|
# File 'app/models/item.rb', line 1102
def image_url2(options = {})
if primary_image
primary_image.image_url(options)
elsif new_item&.primary_image
new_item.primary_image.image_url(options)
end
end
|
#images ⇒ ActiveRecord::Relation<Image>
297
|
# File 'app/models/item.rb', line 297
has_many :images, through: :image_profiles
|
#import_translation_keys ⇒ Object
This method will import existing translations or link to existing ones
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
|
# File 'app/models/item.rb', line 1841
def import_translation_keys
tkrs = []
translations.each do |(locale_string, existing_translations)|
existing_translations.each do |attr, translation|
next unless attr.to_sym.in?(TRANSLATABLE_ATTRIBUTES)
next if translation_key_resources.joins(translation_key: :translations).where(resource_attribute: attr).joins(translation_key: :translations).where(TranslationText[:locale].eq(locale_string)).exists?
next if (text = TranslationKey.newline_normalize(send(:"#{attr}_en"))).blank?
next unless TranslationKey.translatable?(text)
tk = TranslationKey.where(key: text, namespace: TRANSLATION_NAMESPACE).first_or_create!
tk.translations.where(locale: locale_string.first(2)).first_or_create!(text: translation)
tkrs << tk.translation_key_resources.where(resource: self, resource_attribute: attr).first_or_create!
end
end
spec_ids = Item::SpecificationsMasher.new.process(item: self).specifications.values.pluck(:product_specification_id).uniq.compact
specifications_to_import = ProductSpecification.where(id: spec_ids)
tkrs += specifications_to_import.map(&:import_translation_keys).flatten.compact
tkrs
end
|
#increment_request_counter ⇒ Object
771
772
773
774
775
|
# File 'app/models/item.rb', line 771
def increment_request_counter
return unless persisted?
Item.update_counters id, requested_counter: 1 end
|
#inherited_features ⇒ Object
Seek inherited features from the primary product line
To inherit features, a product line's product category must be empty or matching
e.g Tempzone Floor Heating has Heating Elements as a product category
then items belonging to Tempzone Floor Heating must also be heating elements to
inherit its features
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
|
# File 'app/models/item.rb', line 1909
def inherited_features
features_result = []
if (ppl = primary_product_line) && (pc_url = product_category_url)
ppl.self_and_ancestors.select(&:has_features?).each do |pl|
if pl.product_categories.empty? || pl.product_categories.any? { |pc| pc_url.starts_with?(pc.url) }
features_result = pl.features
break
end
end
end
features_result.filter_map(&:presence)
end
|
#is_accessory? ⇒ Object
Alias for Product_category#is_accessory?
640
641
642
643
644
645
646
647
648
649
|
# File 'app/models/item.rb', line 640
delegate :is_control?,
:is_upgrade?,
:is_accessory?,
:is_relay_panel?,
:is_custom_mat?,
:effective_nmfc_code,
:effective_nmfc_class,
:is_publication?,
to: :product_category,
allow_nil: true
|
#is_available_to_public? ⇒ Boolean
798
799
800
801
|
# File 'app/models/item.rb', line 798
def is_available_to_public?
logger.debug "Checking if #{id} / #{sku} is available to public"
orderable_view_product_catalogs.present?
end
|
#is_cable_accessory? ⇒ Boolean
#is_cable_fit_guide? ⇒ Boolean
1370
1371
1372
1373
1374
|
# File 'app/models/item.rb', line 1370
def is_cable_fit_guide?
product_category&.is_accessory? &&
pl_descendant_of_path?(LtreePaths::PL_FLOOR_HEATING_TEMPZONE_THIN_CABLE) &&
name.to_s.downcase.include?('cable fixing plastic strip')
end
|
#is_cable_heating_element? ⇒ Boolean
#is_cable_tape? ⇒ Boolean
1376
1377
1378
1379
1380
|
# File 'app/models/item.rb', line 1376
def is_cable_tape?
product_category&.is_accessory? &&
pl_descendant_of_path?(LtreePaths::PL_FLOOR_HEATING_TEMPZONE_THIN_CABLE) &&
name.to_s.downcase.include?('tape')
end
|
#is_cerazorb? ⇒ Boolean
1325
1326
1327
|
# File 'app/models/item.rb', line 1325
def is_cerazorb?
primary_product_line&.is_cerazorb? && product_category&.is_insulation?
end
|
#is_circuit_check? ⇒ Boolean
1317
1318
1319
|
# File 'app/models/item.rb', line 1317
def is_circuit_check?
sku == ItemConstants::CIRCUIT_CHECK_SKU
end
|
#is_control? ⇒ Object
Alias for Product_category#is_control?
640
641
642
643
644
645
646
647
648
649
|
# File 'app/models/item.rb', line 640
delegate :is_control?,
:is_upgrade?,
:is_accessory?,
:is_relay_panel?,
:is_custom_mat?,
:effective_nmfc_code,
:effective_nmfc_class,
:is_publication?,
to: :product_category,
allow_nil: true
|
#is_cork? ⇒ Boolean
1321
1322
1323
|
# File 'app/models/item.rb', line 1321
def is_cork?
primary_product_line&.is_cork? && product_category&.is_insulation?
end
|
#is_custom_mat? ⇒ Object
Alias for Product_category#is_custom_mat?
640
641
642
643
644
645
646
647
648
649
|
# File 'app/models/item.rb', line 640
delegate :is_control?,
:is_upgrade?,
:is_accessory?,
:is_relay_panel?,
:is_custom_mat?,
:effective_nmfc_code,
:effective_nmfc_class,
:is_publication?,
to: :product_category,
allow_nil: true
|
#is_discontinued_or_publication ⇒ Object
Helper for validation: skips weight checks for discontinued items and publications
695
696
697
|
# File 'app/models/item.rb', line 695
def is_discontinued_or_publication
is_discontinued? || is_publication?
end
|
#is_economy_snow_melt_control? ⇒ Boolean
1341
1342
1343
|
# File 'app/models/item.rb', line 1341
def is_economy_snow_melt_control?
sku == ItemConstants::ECONOMY_SNOW_MELT_CONTROL_SKU
end
|
#is_floor_heating_product? ⇒ Boolean
1127
1128
1129
|
# File 'app/models/item.rb', line 1127
def is_floor_heating_product?
is_heating_element? && pl_descendant_of_path?(LtreePaths::PL_FLOOR_HEATING)
end
|
#is_goods? ⇒ Boolean
1259
1260
1261
|
# File 'app/models/item.rb', line 1259
def is_goods?
tax_class == 'g'
end
|
#is_heating_element? ⇒ Boolean
#is_install_kit? ⇒ Boolean
1143
1144
1145
|
# File 'app/models/item.rb', line 1143
def is_install_kit?
sku&.start_with?(ItemConstants::INSTALL_KIT_SKU_PREFIX)
end
|
#is_junction_box? ⇒ Boolean
1345
1346
1347
|
# File 'app/models/item.rb', line 1345
def is_junction_box?
ItemConstants::JUNCTION_BOX_SKUS.include?(sku)
end
|
#is_led_mirror? ⇒ Boolean
1165
1166
1167
|
# File 'app/models/item.rb', line 1165
def is_led_mirror?
is_mirror? && pl_descendant_of_path?(LtreePaths::PL_LED_MIRROR)
end
|
#is_legacy_relay? ⇒ Boolean
1139
1140
1141
|
# File 'app/models/item.rb', line 1139
def is_legacy_relay?
sku&.start_with?(ItemConstants::LEGACY_RELAY_SKU_PREFIX)
end
|
#is_made_to_order_led_mirror? ⇒ Boolean
1395
1396
1397
1398
1399
|
# File 'app/models/item.rb', line 1395
def is_made_to_order_led_mirror?
primary_product_line&.is_made_to_order_led_mirror? end
|
#is_membrane? ⇒ Boolean
1387
1388
1389
|
# File 'app/models/item.rb', line 1387
def is_membrane?
Item.sku_is_membrane?(sku)
end
|
#is_mirror? ⇒ Boolean
1161
1162
1163
|
# File 'app/models/item.rb', line 1161
def is_mirror?
pc_descendant_of_path?(LtreePaths::PC_MIRRORS)
end
|
#is_mirror_defogger? ⇒ Boolean
#is_oj_programmable_tstat? ⇒ Boolean
#is_oj_programmable_wifi_tstat? ⇒ Boolean
1283
1284
1285
|
# File 'app/models/item.rb', line 1283
def is_oj_programmable_wifi_tstat?
ItemConstants::OJ_PROGRAMMABLE_WIFI_TSTAT_SKU_PREFIXES.any? { |prefix| sku&.start_with?(prefix) }
end
|
#is_pipe_freeze_protection? ⇒ Boolean
#is_power? ⇒ Boolean
1255
1256
1257
|
# File 'app/models/item.rb', line 1255
def is_power?
pc_descendant_of_path?(LtreePaths::PC_POWER)
end
|
#is_power_module? ⇒ Boolean
1135
1136
1137
|
# File 'app/models/item.rb', line 1135
def is_power_module?
ItemConstants::POWER_MODULE_SKU_PREFIXES.any? { |prefix| sku&.include?(prefix) }
end
|
#is_publication? ⇒ Object
Alias for Product_category#is_publication?
640
641
642
643
644
645
646
647
648
649
|
# File 'app/models/item.rb', line 640
delegate :is_control?,
:is_upgrade?,
:is_accessory?,
:is_relay_panel?,
:is_custom_mat?,
:effective_nmfc_code,
:effective_nmfc_class,
:is_publication?,
to: :product_category,
allow_nil: true
|
#is_radiant_panel? ⇒ Boolean
#is_radiant_panel_dual_connect? ⇒ Boolean
1181
1182
1183
|
# File 'app/models/item.rb', line 1181
def is_radiant_panel_dual_connect?
is_radiant_panel? && spec_value(:connection_method) == 'Plug-in or Hardwired'
end
|
#is_radiant_panel_hardwired? ⇒ Boolean
#is_radiant_panel_hardwired_control? ⇒ Boolean
1189
1190
1191
|
# File 'app/models/item.rb', line 1189
def is_radiant_panel_hardwired_control?
sku.in?(ItemConstants::RADIANT_PANEL_HARDWIRE_CONTROL_SKUS)
end
|
#is_radiant_panel_plug_in? ⇒ Boolean
#is_radiant_panel_plug_in_control? ⇒ Boolean
1185
1186
1187
|
# File 'app/models/item.rb', line 1185
def is_radiant_panel_plug_in_control?
sku.in?(ItemConstants::RADIANT_PANEL_PLUG_IN_CONTROL_SKUS)
end
|
#is_radiator? ⇒ Boolean
767
768
769
|
# File 'app/models/item.rb', line 767
def is_radiator?
is_heating_element? || is_towel_warmer? || is_radiant_panel? || is_mirror_defogger?
end
|
#is_refurbished? ⇒ Boolean
1709
1710
1711
|
# File 'app/models/item.rb', line 1709
def is_refurbished?
condition_refurbished?
end
|
#is_relay_panel? ⇒ Object
Alias for Product_category#is_relay_panel?
640
641
642
643
644
645
646
647
648
649
|
# File 'app/models/item.rb', line 640
delegate :is_control?,
:is_upgrade?,
:is_accessory?,
:is_relay_panel?,
:is_custom_mat?,
:effective_nmfc_code,
:effective_nmfc_class,
:is_publication?,
to: :product_category,
allow_nil: true
|
#is_reviewable? ⇒ Boolean
1431
1432
1433
|
# File 'app/models/item.rb', line 1431
def is_reviewable?
Item.reviewable.where(id:).present?
end
|
#is_roughin_kit? ⇒ Boolean
1391
1392
1393
|
# File 'app/models/item.rb', line 1391
def is_roughin_kit?
sku&.include?(ItemConstants::ROUGHIN_KIT_SKU_PATTERN)
end
|
#is_self_regulating_cable? ⇒ Boolean
#is_service? ⇒ Boolean
1267
1268
1269
|
# File 'app/models/item.rb', line 1267
def is_service?
tax_class == 'svc'
end
|
#is_shipping? ⇒ Boolean
1271
1272
1273
|
# File 'app/models/item.rb', line 1271
def is_shipping?
tax_class == 'shp'
end
|
#is_slab_heat_mat? ⇒ Boolean
#is_sm_cable? ⇒ Boolean
1362
1363
1364
|
# File 'app/models/item.rb', line 1362
def is_sm_cable?
is_heating_element? && pl_descendant_of_path?(LtreePaths::PL_SNOW_MELTING_CABLE)
end
|
#is_smartstat? ⇒ Boolean
1275
1276
1277
|
# File 'app/models/item.rb', line 1275
def is_smartstat?
ItemConstants::IID_SMARTSTATS.include?(id)
end
|
#is_snow_melt_plaque? ⇒ Boolean
1337
1338
1339
|
# File 'app/models/item.rb', line 1337
def is_snow_melt_plaque?
sku == ItemConstants::SNOW_MELT_PLAQUE_SKU
end
|
#is_snow_melting_control? ⇒ Boolean
#is_snow_melting_mat? ⇒ Boolean
1313
1314
1315
|
# File 'app/models/item.rb', line 1313
def is_snow_melting_mat?
is_heating_element? && pl_descendant_of_path?(LtreePaths::PL_SNOW_MELTING_MAT)
end
|
#is_snow_melting_product? ⇒ Boolean
1147
1148
1149
|
# File 'app/models/item.rb', line 1147
def is_snow_melting_product?
pl_descendant_of_path?(LtreePaths::PL_SNOW_MELTING)
end
|
#is_spare_parts? ⇒ Boolean
1263
1264
1265
|
# File 'app/models/item.rb', line 1263
def is_spare_parts?
pc_descendant_of_path?(LtreePaths::PC_SPARE_PARTS)
end
|
#is_thermalsheet? ⇒ Boolean
1329
1330
1331
|
# File 'app/models/item.rb', line 1329
def is_thermalsheet?
primary_product_line&.is_thermalsheet? && product_category&.is_insulation?
end
|
#is_thermostat? ⇒ Boolean
#is_towel_warmer? ⇒ Boolean
1219
1220
1221
|
# File 'app/models/item.rb', line 1219
def is_towel_warmer?
pc_descendant_of_path?(LtreePaths::PC_TOWEL_WARMERS)
end
|
#is_towel_warmer_dual_connect? ⇒ Boolean
#is_towel_warmer_hardwired? ⇒ Boolean
#is_towel_warmer_hardwired_control? ⇒ Boolean
1247
1248
1249
|
# File 'app/models/item.rb', line 1247
def is_towel_warmer_hardwired_control?
sku.in?(ItemConstants::TOWEL_WARMER_HARDWIRE_CONTROL_SKUS)
end
|
#is_towel_warmer_plug_in? ⇒ Boolean
#is_towel_warmer_plug_in_control? ⇒ Boolean
1243
1244
1245
|
# File 'app/models/item.rb', line 1243
def is_towel_warmer_plug_in_control?
sku.in?(ItemConstants::TOWEL_WARMER_PLUG_IN_CONTROL_SKUS)
end
|
#is_tz_ruler_cable? ⇒ Boolean
#is_tz_thin_cable? ⇒ Boolean
#is_underlayment? ⇒ Boolean
1333
1334
1335
|
# File 'app/models/item.rb', line 1333
def is_underlayment?
primary_product_line&.is_underlayment? && product_category&.is_insulation?
end
|
#is_upgrade? ⇒ Object
Alias for Product_category#is_upgrade?
640
641
642
643
644
645
646
647
648
649
|
# File 'app/models/item.rb', line 640
delegate :is_control?,
:is_upgrade?,
:is_accessory?,
:is_relay_panel?,
:is_custom_mat?,
:effective_nmfc_code,
:effective_nmfc_class,
:is_publication?,
to: :product_category,
allow_nil: true
|
#item_available_locales ⇒ Object
812
813
814
|
# File 'app/models/item.rb', line 812
def item_available_locales
LocaleUtility::SITE_LOCALES.select { |l| orderable_online_in_locale?(l) }
end
|
#item_group_name(context: :sales) ⇒ Object
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
|
# File 'app/models/item.rb', line 2190
def item_group_name(context: :sales)
pl_slug = case context
when :sales
sales_product_line_slug
when :support
support_product_line_slug
else
raise ArgumentError, "#{context} not supported, :sales or :support expected"
end
return if pl_slug.blank?
return if (pc_slug = product_category_slug).blank?
grouping_elements = [pl_slug.tr('.', '-').tr('_', '-').split('-'), pc_slug.split('-')].flatten
grouping_elements.delete('goods')
grouping_elements = grouping_elements.map(&:singularize)
grouping_elements = grouping_elements.each_with_index.map { |el, i| i < 2 ? el.first(2) : el }
grouping_elements = grouping_elements.compact.uniq
gn = grouping_elements.join('-').parameterize
if gn.size > 50
"#{gn.first(15)}-#{Digest::MD5.hexdigest(gn)}"
else
gn
end
end
|
#item_grouping_info(include_self: false, facet_filters: {}, context: nil) ⇒ Object
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
|
# File 'app/models/item.rb', line 2217
def item_grouping_info(include_self: false, facet_filters: {}, context: nil)
return if product_category.skip_siblings
context ||= :sales
use_pl_path = case context
when :sales
sales_product_line_path
when :support
support_product_line_path
else
raise ArgumentError, "#{context} not supported, :sales or :support expected"
end
use_pc_path = product_category_path
if use_pl_path.present?
variants = Item.active.condition_new.by_product_category_path(use_pc_path).by_product_line_path(use_pl_path).order(:sku)
variants = variants.joins(:product_category).where.not(ProductCategory[:custom_product].eq(true))
group_name = item_group_name(context:)
item_group_id = Digest::MD5.hexdigest(group_name)
facet_filters.each do |facet, value|
variants = variants.with_product_specification(facet, value)
end
else
variants = Item.none
end
variants = variants.where.not(id: id.to_i) unless include_self
result_class = Data.define(:pc_url, :pl_url, :item_group_id, :item_group_name, :variants) do
def initialize(pc_url: nil, pl_url: nil, item_group_id: nil, item_group_name: nil, variants: nil) = super
end
result_class.new(pc_url: use_pc_path, pl_url: use_pl_path, item_group_id:, item_group_name:, variants:)
end
|
#item_product_lines ⇒ ActiveRecord::Relation<ItemProductLine>
271
|
# File 'app/models/item.rb', line 271
has_many :item_product_lines, -> { order(:position) }, inverse_of: :item, dependent: :destroy
|
#item_relations ⇒ ActiveRecord::Relation<PublicationItem>
281
|
# File 'app/models/item.rb', line 281
has_many :item_relations, class_name: 'PublicationItem', foreign_key: :publication_id, inverse_of: :publication, dependent: :destroy
|
#item_state ⇒ Object
788
789
790
791
792
793
794
795
796
|
# File 'app/models/item.rb', line 788
def item_state
if is_discontinued
'Discontinued'
elsif is_available_to_public?
'Published'
else
'Private'
end
end
|
#line_items ⇒ ActiveRecord::Relation<LineItem>
272
|
# File 'app/models/item.rb', line 272
has_many :line_items
|
246
|
# File 'app/models/item.rb', line 246
belongs_to :literature, optional: true
|
#loaded_rendered_product_specifications ⇒ Object
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
|
# File 'app/models/item.rb', line 2046
def loaded_rendered_product_specifications
rp_specs = rendered_product_specifications || {}
ps_ids = rp_specs.values.pluck('product_specification_id').uniq.compact
product_specifications = ProductSpecification.includes(:items, :product_line, :product_category).where(id: ps_ids).index_by(&:id)
rp_specs.each_value do |v|
v['product_specification'] = product_specifications[v['product_specification_id']]
end
rp_specs
end
|
#make_upc_code ⇒ Object
1505
1506
1507
|
# File 'app/models/item.rb', line 1505
def make_upc_code
self.upc = Item::UpcMaker.new.process if upc.blank?
end
|
#missing_catalogs ⇒ Object
2406
2407
2408
|
# File 'app/models/item.rb', line 2406
def missing_catalogs
Catalog.where.not(id: catalog_items.pluck(:catalog_id)).where(store_id: store_items.pluck(:store_id)).where.not(is_discontinued: true).includes(:store).order(Catalog[:id].in([1, 2]).desc).order(Catalog[:name])
end
|
#most_recent_revision(public_only = true) ⇒ Object
1674
1675
1676
1677
1678
|
# File 'app/models/item.rb', line 1674
def most_recent_revision(public_only = true)
items = Item.publications.where('sku ILIKE ?', "#{canonical_sku}%").where('length(sku) <= ?', canonical_sku.size + 2)
items = items.available_to_public if public_only
items.order(sku: :desc).first
end
|
#new_item ⇒ Item
255
|
# File 'app/models/item.rb', line 255
belongs_to :new_item, class_name: 'Item', optional: true
|
#new_revision_sku ⇒ Object
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
|
# File 'app/models/item.rb', line 1689
def new_revision_sku
prev_revs = Item.where('sku ILIKE ?', "#{canonical_sku}%").order(sku: :desc)
sku_match = prev_revs.first.sku.match(/(.*)-([[:alpha:]])$/)
if sku_match && (sku_match.length == 3)
rev_code = sku_match[2]
new_rev = rev_code.succ
"#{canonical_sku}-#{new_rev}"
elsif canonical_sku.present?
"#{canonical_sku}-A"
else
raise "Last revision #{last_rev.id} #{last_rev.sku} could not be parsed for revision (-a, -b, etc.)"
end
end
|
#non_refurbished_sku ⇒ Object
1713
1714
1715
|
# File 'app/models/item.rb', line 1713
def non_refurbished_sku
sku.delete_suffix('-BTK')
end
|
#orderable_online? ⇒ Boolean
943
944
945
946
947
|
# File 'app/models/item.rb', line 943
def orderable_online?
Item.orderable_online.exists?(id:) &&
store_items.active.available.present? &&
catalog_items.active.present?
end
|
#orderable_online_in_locale?(locale = I18n.locale) ⇒ Boolean
804
805
806
807
808
809
810
|
# File 'app/models/item.rb', line 804
def orderable_online_in_locale?(locale = I18n.locale)
scoped_records = orderable_view_product_catalogs if orderable_view_product_catalogs.loaded?
scoped_records ||= orderable_view_product_catalogs.select(:catalog_id)
scoped_records.any? { |vpc| vpc.catalog_id == Catalog.locale_to_catalog_id(locale) }
end
|
#orderable_view_product_catalogs ⇒ ActiveRecord::Relation<ViewProductCatalog>
267
268
269
270
|
# File 'app/models/item.rb', line 267
has_many :orderable_view_product_catalogs,
-> { visible_to_public },
foreign_key: :item_id,
class_name: 'ViewProductCatalog'
|
#packaging_discrepancy_packing ⇒ Packing
258
|
# File 'app/models/item.rb', line 258
belongs_to :packaging_discrepancy_packing, class_name: 'Packing', optional: true
|
#packings ⇒ ActiveRecord::Relation<Packing>
301
|
# File 'app/models/item.rb', line 301
has_and_belongs_to_many :packings, inverse_of: :items
|
#past_model? ⇒ Boolean
1097
1098
1099
|
# File 'app/models/item.rb', line 1097
def past_model?
(tags || []).include?('past-model') || is_discontinued? || primary_product_line&.past_model?
end
|
#pc_descendant_of_path?(path_slug) ⇒ Boolean
Check if item's product category is or descends from the given path
Uses pc_path_slugs stored directly on item (no DB query)
Example: item.pc_descendant_of_path?(LtreePaths::PC_TOWEL_WARMERS)
1211
1212
1213
1214
1215
1216
1217
|
# File 'app/models/item.rb', line 1211
def pc_descendant_of_path?(path_slug)
return false if pc_path_slugs.blank? || path_slug.blank?
slugs = pc_path_slugs.to_s
path = path_slug.to_s
slugs == path || slugs.start_with?("#{path}.")
end
|
#pl_descendant_of_path?(path_slug) ⇒ Boolean
Check if item's primary product line is or descends from the given path
Uses ltree path stored directly on item (no DB query)
Example: item.pl_descendant_of_path?(LtreePaths::PL_FLOOR_HEATING)
1200
1201
1202
1203
1204
1205
1206
|
# File 'app/models/item.rb', line 1200
def pl_descendant_of_path?(path_slug)
return false if primary_pl_path_slugs.blank? || path_slug.blank?
slugs = primary_pl_path_slugs.to_s
path = path_slug.to_s
slugs == path || slugs.start_with?("#{path}.")
end
|
#plan_sensitive? ⇒ Boolean
Plan sensitive items are those tied up to the geometry of an installation plan
1093
1094
1095
|
# File 'app/models/item.rb', line 1093
def plan_sensitive?
is_heating_element?
end
|
#preferred_supplier ⇒ Supplier
252
|
# File 'app/models/item.rb', line 252
belongs_to :preferred_supplier, class_name: 'Supplier', optional: true
|
#primary_image ⇒ Image
248
|
# File 'app/models/item.rb', line 248
belongs_to :primary_image, class_name: 'Image', optional: true
|
#primary_image_url(options = {}) ⇒ Object
Returns the primary image URL for display (e.g., cart toast).
Uses WYS_MAIN profile first, falls back to primary_image association.
1121
1122
1123
1124
1125
|
# File 'app/models/item.rb', line 1121
def primary_image_url(options = {})
wys_main = image_profiles.find { |p| p.image_type == 'WYS_MAIN' }
image = wys_main&.image || primary_image
image&.image_url(options)
end
|
#primary_product_line ⇒ ProductLine
250
|
# File 'app/models/item.rb', line 250
belongs_to :primary_product_line, class_name: 'ProductLine', inverse_of: :primary_items, optional: true
|
#primary_product_line_slug_ltree ⇒ Object
1291
1292
1293
|
# File 'app/models/item.rb', line 1291
def primary_product_line_slug_ltree
primary_product_line&.slug_ltree&.to_s
end
|
243
|
# File 'app/models/item.rb', line 243
belongs_to :product_category
|
#product_category_ancestry_ids ⇒ Object
#product_category_name ⇒ Object
885
886
887
|
# File 'app/models/item.rb', line 885
def product_category_name
ProductCategoryConstants.name_for(product_category_id)
end
|
#product_category_path ⇒ Object
Returns ltree path for product category grouping (preferred - no DB lookup needed)
2154
2155
2156
2157
2158
2159
2160
2161
2162
|
# File 'app/models/item.rb', line 2154
def product_category_path
pc_path = pc_path_slugs.to_s
return LtreePaths::PC_RADIANT_PANELS if pc_path.starts_with?('goods.radiant_panels')
return LtreePaths::PC_TOWEL_WARMERS if pc_path.starts_with?('goods.towel_warmers') && !pc_path.starts_with?('goods.towel_warmers.dual_connection')
return 'goods.towel_warmers.dual_connection' if pc_path.starts_with?('goods.towel_warmers.dual_connection')
pc_path
end
|
#product_category_priority ⇒ Object
889
890
891
|
# File 'app/models/item.rb', line 889
def product_category_priority
ProductCategoryConstants.priority_for(product_category_id)
end
|
#product_category_slug ⇒ Object
Returns product category URL for grouping (legacy - use product_category_path for new code)
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
|
# File 'app/models/item.rb', line 2142
def product_category_slug
use_pc_url = product_category_url
use_pc_url = 'goods-radiant-panels' if use_pc_url.starts_with?('goods-radiant-panels')
if use_pc_url.starts_with?('goods-towel-warmers-dual-connection')
use_pc_url = 'goods-towel-warmers-dual-connection'
elsif use_pc_url.starts_with?('goods-towel-warmers')
use_pc_url = 'goods-towel-warmers'
end
use_pc_url
end
|
#product_category_url ⇒ Object
1296
1297
1298
|
# File 'app/models/item.rb', line 1296
def product_category_url
product_category&.url
end
|
#product_line_ancestry_ids ⇒ Object
#product_line_for_heating_system ⇒ Object
1447
1448
1449
1450
1451
1452
1453
|
# File 'app/models/item.rb', line 1447
def product_line_for_heating_system
(begin
primary_product_line.get_first_heating_system_type
rescue StandardError
nil
end)
end
|
#product_line_for_public_sales_portal ⇒ Object
1439
1440
1441
|
# File 'app/models/item.rb', line 1439
def product_line_for_public_sales_portal
primary_product_line&.get_first_public_for_sales
end
|
#product_line_for_review ⇒ Object
1435
1436
1437
|
# File 'app/models/item.rb', line 1435
def product_line_for_review
primary_product_line&.get_first_reviewable
end
|
#product_line_ids_constellation ⇒ Object
This pulls all product line id including inherited for this item, for the root system,
and complimentary_product_line_ids
779
780
781
782
783
784
785
786
|
# File 'app/models/item.rb', line 779
def product_line_ids_constellation
pl_ids = []
pl_ids += product_line_ids || [] pl_ids += root_system_product_line&.complimentary_product_line_ids || [] pl_ids += primary_product_line&.complimentary_product_line_ids || [] pl_ids = pl_ids.compact.uniq
ProductLine.self_and_ancestors_ids(pl_ids).compact.uniq
end
|
#product_lines ⇒ ActiveRecord::Relation<ProductLine>
273
|
# File 'app/models/item.rb', line 273
has_many :product_lines, through: :item_product_lines
|
#product_specifications(filters = {}) ⇒ Object
Retrieves all product specifications available to a given item, this does not
retrieve rendered specs but the specs definition used to build them
1786
1787
1788
1789
1790
|
# File 'app/models/item.rb', line 1786
def product_specifications(filters = {})
filters[:propagation] ||= %w[0 1] res = Item::SpecificationsRetriever.new.process(self, filters)
res.specifications
end
|
253
|
# File 'app/models/item.rb', line 253
belongs_to :product_tax_code, inverse_of: :items, optional: true
|
#prune_empty_specs ⇒ Object
2112
2113
2114
2115
2116
2117
|
# File 'app/models/item.rb', line 2112
def prune_empty_specs
ds = direct_product_specifications.empty_non_template_specs
ds_count = ds.size
ds.destroy_all
ds_count
end
|
#prune_unused_translations ⇒ Object
2446
2447
2448
2449
2450
2451
|
# File 'app/models/item.rb', line 2446
def prune_unused_translations
valid_locales = content_locales_to_render
translations.each do |(locale, _translation_attribute)|
translations.delete(locale) unless locale.to_sym.in?(valid_locales)
end
end
|
#public_description_html ⇒ Object
1765
1766
1767
1768
1769
|
# File 'app/models/item.rb', line 1765
def public_description_html
(rendered_detailed_description_html.presence || primary_product_line&.description_html.presence)&.html_safe
end
|
#public_description_text ⇒ Object
1771
1772
1773
|
# File 'app/models/item.rb', line 1771
def public_description_text
detailed_description.presence || primary_product_line&.description.presence
end
|
#public_file_name ⇒ Object
897
898
899
|
# File 'app/models/item.rb', line 897
def public_file_name
"#{public_name}.#{Mime::Type.file_extension_of(literature.mime_type) || 'pdf'}"
end
|
#public_name ⇒ Object
893
894
895
|
# File 'app/models/item.rb', line 893
def public_name
public_short_name.presence || name
end
|
#public_specifications ⇒ Object
Only specifications intended for the public are retrieved
1883
1884
1885
|
# File 'app/models/item.rb', line 1883
def public_specifications
specifications.select { |s| s.output.present? && s.grouping != 'Features' && s.grouping != 'Highlights' && s.visibility == 'open_visibility' }
end
|
#publication_relations ⇒ ActiveRecord::Relation<PublicationItem>
280
|
# File 'app/models/item.rb', line 280
has_many :publication_relations, class_name: 'PublicationItem', inverse_of: :item, dependent: :destroy
|
#purchase_order_items ⇒ ActiveRecord::Relation<PurchaseOrderItem>
291
|
# File 'app/models/item.rb', line 291
has_many :purchase_order_items
|
#purge_edge_cache(include_product_line: false) ⇒ Object
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
|
# File 'app/models/item.rb', line 1955
def purge_edge_cache(include_product_line: false)
return :disabled unless Cache::EdgeCacheUtility.edge_cache_enabled?
res = []
urls = all_site_maps.map(&:url)
if urls.present?
Rails.logger.info "Purge Edge Cache called for item id #{id}"
jid = EdgeCacheWorker.perform_async('urls' => urls)
res += urls.map { |url| { url:, jid: } }
end
if include_product_line && (pl = primary_product_line)
res += pl.purge_edge_cache
end
res
end
|
#quantities_indicator_visible? ⇒ Boolean
#refresh_google_feed ⇒ Object
2025
2026
2027
2028
2029
2030
2031
|
# File 'app/models/item.rb', line 2025
def refresh_google_feed
return if (cids = catalog_items.for_google_feed.pluck(:id)).blank?
GoogleFeedGeneratorWorker.perform_async('catalog_item_ids' => catalog_items.for_google_feed.pluck(:id))
cids
end
|
#refurbished_version ⇒ Item
260
|
# File 'app/models/item.rb', line 260
has_one :refurbished_version, class_name: 'Item', foreign_key: :new_item_id
|
287
|
# File 'app/models/item.rb', line 287
has_many :related_source_items, through: :source_item_relations, source: :source_item
|
288
|
# File 'app/models/item.rb', line 288
has_many :related_target_items, through: :target_item_relations, source: :target_item
|
289
|
# File 'app/models/item.rb', line 289
has_many :related_target_items_for_specs, through: :target_item_relations_for_specs, source: :target_item
|
#relatives ⇒ Object
Find all direct siblings and relatives items descendending the product category and product line tree
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
|
# File 'app/models/item.rb', line 2124
def relatives
return Item.none unless persisted? && (product_category_id || primary_product_line_id)
siblings = Item.all
siblings = siblings.by_product_category_id(product_category_id) if product_category
if primary_pl_path_ids.present?
siblings = siblings.where(Item[:primary_pl_path_ids].ltree_descendant(primary_pl_path_ids))
end
siblings = siblings.where.not(id:) if id
siblings.limit(100).order(:sku)
end
|
#render_product_specifications(tokens: nil) ⇒ Object
1927
1928
1929
1930
1931
1932
1933
1934
|
# File 'app/models/item.rb', line 1927
def render_product_specifications(tokens: nil)
res = Item::SpecificationsMasher.new.process(item: self, tokens:).specifications
if tokens.present? (rendered_product_specifications || {}).merge(res)
else
res
end
end
|
#rendered_detailed_description_html ⇒ Object
1761
1762
1763
|
# File 'app/models/item.rb', line 1761
def rendered_detailed_description_html
@rendered_detailed_description_html ||= (detailed_description_html.present? ? detailed_description_html_template_instance.render(token_specs_values_for_liquid(include_legacy: true)) : nil)
end
|
#rendered_product_specifications_grouped(locale: Mobility.locale, filter_grouping: nil, sort_order: []) ⇒ Object
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
|
# File 'app/models/item.rb', line 2058
def rendered_product_specifications_grouped(locale: Mobility.locale, filter_grouping: nil, sort_order: [])
Mobility.with_locale(locale) do
filtered = loaded_rendered_product_specifications
.select { |_k, v| filter_grouping.nil? || v['grouping'] == filter_grouping }
sorted = filtered.sort_by do |k, v|
[
v['grouping'] || nil, sort_order.index(k.to_sym) || Float::INFINITY, v['name'] || nil ]
end
sorted.group_by { |_k, v| v['grouping'] || 'Other' }
end
end
|
#replacement_for ⇒ Item
249
|
# File 'app/models/item.rb', line 249
belongs_to :replacement_for, class_name: 'Item', optional: true
|
#requires_distributor_id_code? ⇒ Boolean
1287
1288
1289
|
# File 'app/models/item.rb', line 1287
def requires_distributor_id_code?
(sku&.start_with?('UWG4-4999') || sku&.start_with?('AWG4-4999')) && !sku&.include?('-WY')
end
|
#retrieve_faqs ⇒ Object
669
670
671
|
# File 'app/models/item.rb', line 669
def retrieve_faqs
Item::ArticleRetriever.new(article_types: %w[faq]).process(item: self).articles
end
|
#returned_rma_items ⇒ ActiveRecord::Relation<Rma>
274
|
# File 'app/models/item.rb', line 274
has_many :returned_rma_items, class_name: 'Rma', foreign_key: :returned_item_id
|
#revised_publication_for_public ⇒ Object
970
971
972
973
|
# File 'app/models/item.rb', line 970
def revised_publication_for_public
revision = most_recent_revision(:public)
revision == self ? nil : revision
end
|
#rma_reason_codes(include_advanced: true) ⇒ Object
901
902
903
904
905
906
907
|
# File 'app/models/item.rb', line 901
def rma_reason_codes(include_advanced: true)
rrcs = RmaReasonCode.active
rrcs = rrcs.for_product_category_ids(product_category_id).for_product_line_ids(primary_product_line_id) if product_category_id && primary_product_line_id
rrcs = rrcs.where(advanced_reason_code: false) unless include_advanced
rrcs
end
|
#root_system_product_line ⇒ Object
Alias for Primary_product_line#root_system_product_line
651
652
653
|
# File 'app/models/item.rb', line 651
delegate :root_system_product_line,
to: :primary_product_line,
allow_nil: true
|
Run shipping audit for all boxes, optionally for a specific carrier
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
|
# File 'app/models/item.rb', line 1643
def run_shipping_audit(carrier: nil)
carriers = carrier.present? ? [carrier] : nil
r = []
r1 = Shipping::PackageAuditor.new
r1.process(shipping_dimensions_to_package, carriers:)
r2 = r3 = nil
if box2_defined?
r2 = Shipping::PackageAuditor.new
r2.process(box2_shipping_dimensions_to_package, carriers:)
end
if box3_defined?
r3 = Shipping::PackageAuditor.new
r3.process(box3_shipping_dimensions_to_package, carriers:)
end
r << r1
r << r2 if r2
r << r3 if r3
r
end
|
#safe_name ⇒ Object
Returns a name suitable for edit feed with any special character that will
destroy a csv formatting
826
827
828
829
830
831
832
|
# File 'app/models/item.rb', line 826
def safe_name
return if name.blank?
name.gsub("'", 'ft')
.gsub('"', 'in')
.gsub(%r{[^0-9A-Za-z.-/]}, ' ').squish
end
|
#sales_product_line_path ⇒ Object
Returns ltree path for sales product line (preferred - reuses already-loaded ProductLine)
2171
2172
2173
2174
2175
|
# File 'app/models/item.rb', line 2171
def sales_product_line_path
pl_path = primary_product_line&.get_first_show_in_sales_portal_ancestor_path
pl_path ||= product_lines.map(&:get_first_show_in_sales_portal_ancestor_path).first
pl_path
end
|
#sales_product_line_slug ⇒ Object
2164
2165
2166
2167
2168
|
# File 'app/models/item.rb', line 2164
def sales_product_line_slug
slt = primary_product_line&.get_first_show_in_sales_portal_ancestor_slug_ltree
slt ||= product_lines.map(&:get_first_show_in_sales_portal_ancestor_slug_ltree).first
slt
end
|
251
|
# File 'app/models/item.rb', line 251
belongs_to :secondary_product_category, class_name: 'ProductCategory', optional: true
|
#selection_name ⇒ Object
1739
1740
1741
|
# File 'app/models/item.rb', line 1739
def selection_name
"#{sku} - #{name}#{' [Discontinued]' if is_discontinued?}"
end
|
#serial_numbers ⇒ ActiveRecord::Relation<SerialNumber>
286
|
# File 'app/models/item.rb', line 286
has_many :serial_numbers, through: :store_items
|
#set_grouping(force: false) ⇒ Object
Records a grouping string identifier, this speeds up variant grouping lookup for
google and reviews.io api calls.
2289
2290
2291
2292
2293
2294
2295
|
# File 'app/models/item.rb', line 2289
def set_grouping(force: false)
return unless is_goods? || is_service?
return if is_publication?
return unless force || product_category_id_changed? || primary_product_line_id_changed?
self.grouping = item_group_name
end
|
#set_packaged_items_md5_hash(options = {}) ⇒ Object
2279
2280
2281
2282
2283
2284
2285
|
# File 'app/models/item.rb', line 2279
def set_packaged_items_md5_hash(options = {})
return unless packings.from_item.empty? || packings.from_item.any?(&:invalid?) || any_box_changed? || options[:force]
Shipping::ItemMd5Extractor.new.process(self)
end
|
#shift_images(locale: 'en', prefix: nil) ⇒ Object
Shifts images up to fill empty slots in the image type order for this item
This method will move images to fill gaps in the sequence, maintaining the proper order
#shipping_dimensions(unit: 'in', precision: 0) ⇒ Object
unit_system SI = Standard International, e.g Metric
ucs = United States Customary Unit
1523
1524
1525
1526
1527
1528
1529
1530
1531
|
# File 'app/models/item.rb', line 1523
def shipping_dimensions(unit: 'in', precision: 0)
return unless shipping_length && shipping_width && shipping_height
[
shipping_length_converted(unit:, precision:),
shipping_width_converted(unit:, precision:),
shipping_height_converted(unit:, precision:)
]
end
|
#shipping_dimensions_display ⇒ Object
1069
1070
1071
|
# File 'app/models/item.rb', line 1069
def shipping_dimensions_display
"#{shipping_length}″ (L) x #{shipping_width}″ (W) x #{shipping_height}″ (H)"
end
|
#shipping_dimensions_to_package ⇒ Object
1619
1620
1621
1622
1623
1624
|
# File 'app/models/item.rb', line 1619
def shipping_dimensions_to_package
return unless box1_defined?
Shipping::Container.new(length: shipping_length, width: shipping_width, height: shipping_height, weight: shipping_weight,
container_type:)
end
|
#shipping_height_converted(unit: 'in', precision: 0) ⇒ Object
1545
1546
1547
1548
1549
|
# File 'app/models/item.rb', line 1545
def shipping_height_converted(unit: 'in', precision: 0)
return unless shipping_height
RubyUnits::Unit.new("#{shipping_height} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#shipping_length_converted(unit: 'in', precision: 0) ⇒ Object
1533
1534
1535
1536
1537
|
# File 'app/models/item.rb', line 1533
def shipping_length_converted(unit: 'in', precision: 0)
return unless shipping_length
RubyUnits::Unit.new("#{shipping_length} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#shipping_options ⇒ ActiveRecord::Relation<ShippingOption>
275
|
# File 'app/models/item.rb', line 275
has_many :shipping_options
|
#shipping_volume ⇒ Object
1042
1043
1044
1045
1046
|
# File 'app/models/item.rb', line 1042
def shipping_volume
boxes.sum { |b| b[0] * b[1] * b[2] }
rescue StandardError
0
end
|
#shipping_volume_in_cubic_feet ⇒ Object
1048
1049
1050
1051
1052
|
# File 'app/models/item.rb', line 1048
def shipping_volume_in_cubic_feet
res = nil
res = shipping_volume / 1728.0 if shipping_volume.present? && shipping_volume.positive?
res
end
|
#shipping_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
1607
1608
1609
|
# File 'app/models/item.rb', line 1607
def shipping_weight_converted(unit: 'lbs', precision: nil)
express_weight_converted shipping_weight, unit:, precision:
end
|
#shipping_width_converted(unit: 'in', precision: 0) ⇒ Object
1539
1540
1541
1542
1543
|
# File 'app/models/item.rb', line 1539
def shipping_width_converted(unit: 'in', precision: 0)
return unless shipping_width
RubyUnits::Unit.new("#{shipping_width} in").convert_to(unit.to_s).scalar.to_f.round(precision)
end
|
#ships_in_single_box? ⇒ Boolean
2410
2411
2412
|
# File 'app/models/item.rb', line 2410
def ships_in_single_box?
!(box2_defined? || box3_defined?)
end
|
#ships_via_crate? ⇒ Boolean
2330
2331
2332
|
# File 'app/models/item.rb', line 2330
def ships_via_crate?
is_made_to_order_led_mirror?
end
|
#ships_via_freight? ⇒ Boolean
1405
1406
1407
1408
|
# File 'app/models/item.rb', line 1405
def ships_via_freight?
freight_service?
end
|
#siblings ⇒ Object
929
930
931
932
933
|
# File 'app/models/item.rb', line 929
def siblings
return Item.none unless primary_product_line_id
Item.by_product_line_id(primary_product_line_id).where.not(id:)
end
|
#single_item_packings_from_md5 ⇒ Object
Finds packings of this item but only for a single quantity
2322
2323
2324
2325
2326
2327
2328
|
# File 'app/models/item.rb', line 2322
def single_item_packings_from_md5
if (md5 = Shipping::Md5HashItem.process(self).md5)
packings.where(md5:)
else
Packing.none
end
end
|
#site_maps ⇒ ActiveRecord::Relation<SiteMap>
284
|
# File 'app/models/item.rb', line 284
has_many :site_maps, as: :resource, dependent: :destroy
|
#sku_and_name ⇒ Object
1088
1089
1090
|
# File 'app/models/item.rb', line 1088
def sku_and_name
"#{sku} - #{name}"
end
|
#source_item_relations ⇒ ActiveRecord::Relation<ItemRelation>
276
|
# File 'app/models/item.rb', line 276
has_many :source_item_relations, class_name: 'ItemRelation', foreign_key: :target_item_id, dependent: :destroy
|
#specific_items ⇒ ActiveRecord::Relation<Item>
283
|
# File 'app/models/item.rb', line 283
has_many :specific_items, through: :item_relations, class_name: 'Item', source: :item
|
#specific_publications ⇒ ActiveRecord::Relation<Item>
282
|
# File 'app/models/item.rb', line 282
has_many :specific_publications, through: :publication_relations, class_name: 'Item', source: :publication
|
#specifications ⇒ Object
Retrieves rendered specifications as an object
1874
1875
1876
|
# File 'app/models/item.rb', line 1874
def specifications
rendered_product_specifications&.values&.map { |s| OpenStruct.new(s) } || []
end
|
#specifications_for_item_label ⇒ Object
1887
1888
1889
|
# File 'app/models/item.rb', line 1887
def specifications_for_item_label
specifications.select { |rps| rps.print_on_item_label && rps.output.present? }.sort_by(&:name)
end
|
#specifications_grouped ⇒ Object
1878
1879
1880
|
# File 'app/models/item.rb', line 1878
def specifications_grouped
specifications.sort_by { |s| [s.grouping, s.name] }.group_by(&:grouping)
end
|
#star? ⇒ Boolean
Makes a star appear in the document library
835
836
837
|
# File 'app/models/item.rb', line 835
def star?
(tags || []).intersect?(%w[Star star])
end
|
#store_items ⇒ ActiveRecord::Relation<StoreItem>
262
|
# File 'app/models/item.rb', line 262
has_many :store_items, inverse_of: :item, dependent: :destroy
|
#successor_item ⇒ Item
254
|
# File 'app/models/item.rb', line 254
belongs_to :successor_item, class_name: 'Item', optional: true
|
#suggested_item_applies_to ⇒ Object
Alias for Suggested_item_tool#suggested_item_applies_to
655
656
657
|
# File 'app/models/item.rb', line 655
delegate :suggested_item_stock_threshold,
:suggested_item_applies_to,
to: :suggested_item_tool
|
#suggested_item_stock_threshold ⇒ Object
Alias for Suggested_item_tool#suggested_item_stock_threshold
655
656
657
|
# File 'app/models/item.rb', line 655
delegate :suggested_item_stock_threshold,
:suggested_item_applies_to,
to: :suggested_item_tool
|
#superceded_items ⇒ ActiveRecord::Relation<Item>
265
|
# File 'app/models/item.rb', line 265
has_many :superceded_items, class_name: 'Item', foreign_key: :successor_item_id
|
244
|
# File 'app/models/item.rb', line 244
belongs_to :supplier_item, inverse_of: :items, optional: true
|
#supplier_items ⇒ ActiveRecord::Relation<SupplierItem>
277
|
# File 'app/models/item.rb', line 277
has_many :supplier_items, inverse_of: :item, dependent: :destroy
|
#supplier_skus ⇒ Object
816
817
818
|
# File 'app/models/item.rb', line 816
def supplier_skus
([secondary_model_number] + supplier_items.active.distinct.pluck(:supplier_sku)).filter_map(&:presence).uniq
end
|
#support_product_line_path ⇒ Object
Returns ltree path for support product line (preferred - reuses already-loaded ProductLine)
2184
2185
2186
2187
2188
|
# File 'app/models/item.rb', line 2184
def support_product_line_path
pl_path = primary_product_line&.get_first_show_in_support_portal_ancestor_path
pl_path ||= product_lines.map(&:get_first_show_in_support_portal_ancestor_path).first
pl_path
end
|
#support_product_line_slug ⇒ Object
2177
2178
2179
2180
2181
|
# File 'app/models/item.rb', line 2177
def support_product_line_slug
slt = primary_product_line&.get_first_show_in_support_portal_ancestor_slug_ltree
slt ||= product_lines.map(&:get_first_show_in_support_portal_ancestor_slug_ltree).first
slt
end
|
665
666
667
|
# File 'app/models/item.rb', line 665
def tags_display
tags.join(', ')
end
|
#target_item_relations ⇒ ActiveRecord::Relation<ItemRelation>
278
|
# File 'app/models/item.rb', line 278
has_many :target_item_relations, class_name: 'ItemRelation', foreign_key: :source_item_id, dependent: :destroy
|
#target_item_relations_for_specs ⇒ ActiveRecord::Relation<ItemRelation>
279
|
# File 'app/models/item.rb', line 279
has_many :target_item_relations_for_specs, -> { where(include_in_spec: true) }, class_name: 'ItemRelation', foreign_key: :source_item_id
|
#third_party_skus(limit: 1) ⇒ Object
820
821
822
|
# File 'app/models/item.rb', line 820
def third_party_skus(limit: 1)
(catalog_items.where.not(third_party_sku: nil).where.not(CatalogItem[:third_party_sku].matches(sku)).pluck(:third_party_sku) - [sku]).filter_map(&:presence).uniq.sort.first(limit)
end
|
#thumbnail_url(size: '30x30') ⇒ Object
1115
1116
1117
|
# File 'app/models/item.rb', line 1115
def thumbnail_url(size: '30x30')
image_url2(size:, thumbnail: true)
end
|
#to_liquid ⇒ Object
2463
2464
2465
|
# File 'app/models/item.rb', line 2463
def to_liquid
Liquid::ItemDrop.new self
end
|
#to_packdims ⇒ Object
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
|
# File 'app/models/item.rb', line 2297
def to_packdims
sle, swi, she = [[shipping_length.ceil(1), 0.1].max, [shipping_width.ceil(1), 0.1].max, [shipping_height.ceil(1), 0.1].max].sort.reverse
swg = [shipping_weight.ceil(2), 0.01].max
packdims = [[sle, swi, she, swg, Shipment.container_types[container_type]]]
if box2_defined?
box2_sle, box2_swi, box2_she = [[box2_shipping_length.ceil(1), 0.1].max, [box2_shipping_width.ceil(1), 0.1].max, [box2_shipping_height.ceil(1), 0.1].max].sort.reverse
box2_swg = [box2_shipping_weight.ceil(2), 0.01].max
packdims << [box2_sle, box2_swi, box2_she, box2_swg, Shipment.container_types[container_type]]
end
if box3_defined?
box3_sle, box3_swi, box3_she = [[box3_shipping_length.ceil(1), 0.1].max, [box3_shipping_width.ceil(1), 0.1].max, [box3_shipping_height.ceil(1), 0.1].max].sort.reverse
box3_swg = [box3_shipping_weight.ceil(2), 0.01].max
packdims << [box3_sle, box3_swi, box3_she, box3_swg, Shipment.container_types[container_type]]
end
packdims
end
|
#to_s ⇒ Object
1733
1734
1735
1736
1737
|
# File 'app/models/item.rb', line 1733
def to_s
s = "#{sku} - #{name}"
s << ' (Discontinued)' if is_discontinued?
s
end
|
#total_shipping_weight ⇒ Object
2453
2454
2455
|
# File 'app/models/item.rb', line 2453
def total_shipping_weight
boxes.sum { |b| b[3].to_f }
end
|
#total_shipping_weight_converted(unit: 'lbs', precision: nil) ⇒ Object
1603
1604
1605
|
# File 'app/models/item.rb', line 1603
def total_shipping_weight_converted(unit: 'lbs', precision: nil)
express_weight_converted total_shipping_weight, unit:, precision:
end
|
#total_shipping_weight_of_kit_components ⇒ Object
2457
2458
2459
2460
2461
|
# File 'app/models/item.rb', line 2457
def total_shipping_weight_of_kit_components
return total_shipping_weight unless is_kit?
kit_target_item_relations.includes(:target_item).sum { |tir| tir.quantity * tir.target_item.total_shipping_weight }
end
|
#translation_key_resources ⇒ ActiveRecord::Relation<TranslationKeyResource>
292
|
# File 'app/models/item.rb', line 292
has_many :translation_key_resources, as: :resource, dependent: :destroy
|
#update_boxes_from_packages(packages) ⇒ Object
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
|
# File 'app/models/item.rb', line 1011
def update_boxes_from_packages(packages)
self.shipping_length = packages[0].try(:length)
self.shipping_width = packages[0].try(:width)
self.shipping_height = packages[0].try(:height)
self.shipping_weight = packages[0].try(:weight)
if packages.size > 1
self.box2_shipping_length = packages[1].try(:length)
self.box2_shipping_width = packages[1].try(:width)
self.box2_shipping_height = packages[1].try(:height)
self.box2_shipping_weight = packages[1].try(:weight)
else
self.box2_shipping_length = nil
self.box2_shipping_width = nil
self.box2_shipping_height = nil
self.box2_shipping_weight = nil
end
if packages.size > 2
self.box3_shipping_length = packages[2].try(:length)
self.box3_shipping_width = packages[2].try(:width)
self.box3_shipping_height = packages[2].try(:height)
self.box3_shipping_weight = packages[2].try(:weight)
else
self.box3_shipping_length = nil
self.box3_shipping_width = nil
self.box3_shipping_height = nil
self.box3_shipping_weight = nil
end
save
end
|
#update_rendered_product_specifications(user_id: nil, tokens: nil, locales: nil, skip_google_feed: true) ⇒ Object
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
|
# File 'app/models/item.rb', line 1993
def update_rendered_product_specifications(user_id: nil, tokens: nil, locales: nil, skip_google_feed: true)
return :skipped if new_record? || is_publication?
locales ||= content_locales_to_render
self.refresh_specs = false self.skip_item_calc = true locales.uniq.each do |locale|
Mobility.with_locale(locale) do
self.rendered_product_specifications = render_product_specifications(tokens:)
end
end
self.rendered_product_specifications_at = Time.current
prune_unused_translations
res = :fail
CurrentScope.with_user_id(user_id) do
res = :success if save
end
if res == :success
refresh_google_feed unless skip_google_feed purge_edge_cache(include_product_line: false) end
res
end
|
245
|
# File 'app/models/item.rb', line 245
belongs_to :upload, optional: true
|
#uploads ⇒ ActiveRecord::Relation<Upload>
303
|
# File 'app/models/item.rb', line 303
has_and_belongs_to_many :uploads, dependent: :destroy
|
#url_paths ⇒ Object
711
712
713
714
715
716
|
# File 'app/models/item.rb', line 711
def url_paths
p = []
p += primary_product_line.url_paths
p << sku
p.compact
end
|
#variants(include_self: false) ⇒ Object
A more strict version of relatives, we don't look at descendants, only exact matches
2137
2138
2139
|
# File 'app/models/item.rb', line 2137
def variants(include_self: false)
item_grouping_info(include_self:)&.variants || Item.none
end
|
#versions_for_audit_trail(_params = {}) ⇒ Object
2475
2476
2477
2478
2479
2480
2481
2482
2483
|
# File 'app/models/item.rb', line 2475
def versions_for_audit_trail(_params = {})
RecordVersion.where(<<~SQL.squish, id:)
(item_type = 'Item' AND item_id = :id)
OR (
item_type = 'ImageProfile'
AND reference_data @> '{"item_id": :id}'
)
SQL
end
|
#view_product_catalogs ⇒ ActiveRecord::Relation<ViewProductCatalog>
266
|
# File 'app/models/item.rb', line 266
has_many :view_product_catalogs
|
#warm_cache(include_product_line: true) ⇒ Object
Finds the associated sitemap (url) end points of this item and its product line
Perform a cache warmup
1946
1947
1948
1949
1950
1951
1952
1953
|
# File 'app/models/item.rb', line 1946
def warm_cache(include_product_line: true)
return :disabled unless Cache::EdgeCacheUtility.edge_cache_enabled?
all_site_maps.each(&:warm_cache)
return unless include_product_line && (pl = primary_product_line)
pl.warm_cache
end
|
#will_be_restricted_for_sales? ⇒ Boolean
689
690
691
692
|
# File 'app/models/item.rb', line 689
def will_be_restricted_for_sales?
restricted_for_sales ||
(product_category&.restricted_for_sales && primary_product_line&.restricted_for_sales)
end
|