Class: CatalogItem
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- CatalogItem
- Includes:
- Memery, Models::Auditable, Models::CatalogItemAmazonHelper, Models::CatalogItemWalmartHelper, Models::CatalogItemWayfairHelper, Models::SaleDiscountable, Models::Translatable, OrderQuery, PgSearch::Model
- Defined in:
- app/models/catalog_item.rb
Overview
== Schema Information
Table name: catalog_items
Database name: primary
id :integer not null, primary key
alternate_warehouse_stock_reporting_max :integer
amazon_desired_product_type :string
amazon_fba_labeling :enum
amazon_fba_sku :string
amazon_fnsku :string
amazon_info_datetime :datetime
amazon_item_suppression_pull_datetime :datetime
amazon_listing_reported_product_type :string
amazon_reported_product_type :string
amount :decimal(8, 2)
amz_last_buy_box_winner_change :datetime
amz_min_seller_price_override :decimal(8, 2)
backup_tppn :string
business_discount_override :integer
clearance :boolean default(FALSE), not null
disable_amz_repricing :boolean default(FALSE), not null
discontinued_date :date
discontinued_part_number :string
google_feed :boolean default(FALSE), not null
google_feed_title :string
is_amz_buy_box_winner :boolean default(FALSE), not null
is_amz_featured_merchant :boolean default(FALSE), not null
item_suppressed_status :string
item_suppressed_status_message :string
last_inventory_advice_sent_json :jsonb
last_price_advice_sent_json :jsonb
legacy_fulfillment :integer default(0), not null
legacy_sale_price_effective_date :date
legacy_sale_price_expiration_date :date
margin_required :decimal(, )
max_discount :integer default(100)
min_stock_to_report :integer
new_price :decimal(8, 2)
new_price_effective_date :date
new_sale_price :decimal(8, 2)
old_amount :decimal(10, 2)
pack_at_kit_level :boolean default(FALSE), not null
parent_sku :string
pending_discontinue_date :date
position :integer default(100)
price_editable :boolean default(FALSE), not null
price_updated_at :datetime
quantity_1_lower_bound :integer
quantity_1_price_discount :decimal(6, 2)
quantity_1_price_type :enum
quantity_2_lower_bound :integer
quantity_2_price_discount :decimal(6, 2)
quantity_2_price_type :enum
quantity_3_lower_bound :integer
quantity_3_price_discount :decimal(6, 2)
quantity_3_price_type :enum
quantity_4_lower_bound :integer
quantity_4_price_discount :decimal(6, 2)
quantity_4_price_type :enum
quantity_5_lower_bound :integer
quantity_5_price_discount :decimal(6, 2)
quantity_5_price_type :enum
quantity_discount_price_type :enum
reserve_stock :integer
retail_price :decimal(8, 2)
retailer_information :jsonb
retailer_price_updated_at :datetime
retailer_requested_cost :decimal(10, 2)
sale_price :decimal(8, 2)
skip_url_checks :boolean default(FALSE), not null
state :string(30) default("active"), not null
third_party_description :text
third_party_name :string(255)
third_party_part_number :string(255)
third_party_product_type :string
third_party_promo_part_number :string
third_party_short_description :text
third_party_sku :string
translations :jsonb
url :string
url_last_checked :datetime
url_valid :boolean default(FALSE), not null
created_at :datetime
updated_at :datetime
amazon_variation_id :bigint
catalog_id :integer
coupon_id :integer
new_coupon_id :integer
store_item_id :integer not null
Indexes
index_catalog_items_on_amazon_fnsku (amazon_fnsku) UNIQUE
index_catalog_items_on_amazon_variation_id (amazon_variation_id)
index_catalog_items_on_catalog_id_and_store_item_id (catalog_id,store_item_id) UNIQUE
index_catalog_items_on_catalog_id_and_third_party_sku (catalog_id,third_party_sku) UNIQUE
index_catalog_items_on_catalog_id_and_tpn (catalog_id,third_party_promo_part_number) UNIQUE
index_catalog_items_on_coupon_id (coupon_id)
index_catalog_items_on_new_coupon_id (new_coupon_id)
index_catalog_items_on_parent_sku (parent_sku)
index_catalog_items_on_state_and_store_item_id_and_catalog_id (state,store_item_id,catalog_id)
index_catalog_items_on_state_and_url_valid (state,url_valid)
index_catalog_items_on_store_item_id_and_state (store_item_id,state)
index_catalog_items_on_third_party_part_number_and_catalog_id (third_party_part_number,catalog_id) UNIQUE WHERE (third_party_part_number IS NOT NULL)
index_catalog_items_on_third_party_sku_and_catalog_id (third_party_sku,catalog_id) UNIQUE WHERE (third_party_sku IS NOT NULL)
Foreign Keys
catalog_items_catalog_id_fkey (catalog_id => catalogs.id)
catalog_items_store_item_id_fk (store_item_id => store_items.id) ON DELETE => cascade
fk_rails_... (amazon_variation_id => amazon_variations.id)
fk_rails_... (coupon_id => coupons.id)
fk_rails_... (new_coupon_id => coupons.id) ON DELETE => nullify
Defined Under Namespace
Classes: CouponAndSalePriceValidator, LegacyPullAmazonCatalogItemListingsData, LegacySetAmazonCatalogItemPricesFromListingsData, Onboarder, PriceSync, PriceUpdatedHandler, QuantityBoundsValidator, Remapper, ScheduledPriceChanger
Constant Summary collapse
- HIDDEN_STATES =
%w[active_hidden discontinued pending_client_review invalid_catalog_item pending_onboarding].freeze
- EDI_FEED_STATUSES =
%w[active require_vendor_update pending_onboarding pending_vendor_update pending_discontinue].freeze
- EDI_DELTA_FEED_CATEGORIES =
Omitting an item from a feed automatically will trigger a removal, therefore pending_discontinue should be excluded
from selection %w[inventory_advice price_advice]
- ORCHESTRATOR_STATES =
%w[active require_vendor_update pending_vendor_update pending_discontinue discontinued].freeze
Constants included from Models::CatalogItemAmazonHelper
Models::CatalogItemAmazonHelper::AMAZON_DEFAULT_BUSINESS_DISCOUNT, Models::CatalogItemAmazonHelper::AMAZON_MIN_PROFIT_MARGIN
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
-
#add_kit_components_to_catalog ⇒ Object
Returns the value of attribute add_kit_components_to_catalog.
- #alternate_warehouse_stock_reporting_max ⇒ Object readonly
- #amount ⇒ Object readonly
- #item_id ⇒ Object
- #max_discount ⇒ Object readonly
- #new_price ⇒ Object readonly
- #new_price_effective_date ⇒ Object readonly
-
#new_sale_price_with_vat ⇒ Object
Returns the value of attribute new_sale_price_with_vat.
-
#price_with_vat ⇒ Object
Returns the value of attribute price_with_vat.
- #reserve_stock ⇒ Object readonly
-
#sale_price_with_vat ⇒ Object
Returns the value of attribute sale_price_with_vat.
-
#skip_check_kit_components ⇒ Object
Returns the value of attribute skip_check_kit_components.
-
#skip_create_kit_components ⇒ Object
Returns the value of attribute skip_create_kit_components.
- #store_ids ⇒ Object
- #third_party_name_en ⇒ Object readonly
- #third_party_name_fr ⇒ Object readonly
-
#third_party_part_number ⇒ Object
readonly
validates :store_item_id, uniqueness: { scope: :catalog_id }.
- #third_party_sku ⇒ Object readonly
Attributes included from Models::CatalogItemAmazonHelper
Attributes included from Models::Translatable
#do_not_compact_translation_container
Belongs to collapse
Methods included from Models::Auditable
Has one collapse
- #amazon_marketplace ⇒ AmazonMarketplace
- #exported_catalog_item ⇒ ExportedCatalogItem
- #item ⇒ Item
- #store ⇒ Store
Has many collapse
- #amazon_catalog_item_flags ⇒ ActiveRecord::Relation<AmazonCatalogItemFlag>
- #edi_communication_logs ⇒ ActiveRecord::Relation<EdiCommunicationLog>
- #edi_documents ⇒ ActiveRecord::Relation<EdiDocument>
- #google_feeds ⇒ ActiveRecord::Relation<GoogleFeed>
- #line_items ⇒ ActiveRecord::Relation<LineItem>
- #retailer_probes ⇒ ActiveRecord::Relation<CatalogItemRetailerProbe>
- #site_maps ⇒ ActiveRecord::Relation<SiteMap>
Has and belongs to many collapse
- #amazon_browse_nodes ⇒ ActiveRecord::Relation<AmazonBrowseNode>
- #store_items ⇒ ActiveRecord::Relation<StoreItem>
Delegated Instance Attributes collapse
-
#all_amazon_image_profiles ⇒ Object
Alias for to: :item#all_amazon_image_profiles.
-
#currency ⇒ Object
Alias for Catalog#currency.
-
#currency_symbol ⇒ Object
Alias for Catalog#currency_symbol.
-
#facet_tokens ⇒ Object
Alias for to: :item#facet_tokens.
-
#is_available_to_public ⇒ Object
Alias for to: :item#is_available_to_public.
-
#is_cable_accessory? ⇒ Object
Alias for to: :item#is_cable_accessory?.
-
#is_cable_fit_guide? ⇒ Object
Alias for to: :item#is_cable_fit_guide?.
-
#is_circuit_check? ⇒ Object
Alias for to: :item#is_circuit_check?.
-
#is_control? ⇒ Object
Alias for to: :item#is_control?.
-
#is_heating_element? ⇒ Object
Alias for to: :item#is_heating_element?.
-
#is_membrane? ⇒ Object
Alias for to: :item#is_membrane?.
-
#is_publication? ⇒ Object
Alias for to: :item#is_publication?.
-
#is_roughin_kit? ⇒ Object
Alias for to: :item#is_roughin_kit?.
-
#is_snow_melt_plaque? ⇒ Object
Alias for to: :item#is_snow_melt_plaque?.
-
#is_underlayment? ⇒ Object
Alias for to: :item#is_underlayment?.
-
#oversize? ⇒ Object
Alias for to: :item#oversize?.
-
#parent_catalog ⇒ Object
Alias for Catalog#parent_catalog.
-
#prefix: :new_sale_price_effective_date ⇒ Object
Alias for New_coupon#effective_date.
-
#prefix: :new_sale_price_expiration_date ⇒ Object
Alias for New_coupon#expiration_date.
-
#prefix: :sale_price_effective_date ⇒ Object
Alias for Coupon#effective_date.
-
#prefix: :sale_price_expiration_date ⇒ Object
Alias for Coupon#expiration_date.
-
#primary_image ⇒ Object
Alias for to: :item#primary_image.
-
#primary_product_line ⇒ Object
Alias for to: :item#primary_product_line.
-
#primary_product_line_id ⇒ Object
Alias for to: :item#primary_product_line_id.
-
#product_category ⇒ Object
Alias for to: :item#product_category.
-
#product_lines ⇒ Object
Alias for to: :item#product_lines.
-
#public_description_html ⇒ Object
Alias for to: :item#public_description_html.
-
#public_name ⇒ Object
Alias for to: :item#public_name.
-
#qty_available ⇒ Object
Alias for Store_item#qty_available.
-
#qty_available_outside_order ⇒ Object
Alias for Store_item#qty_available_outside_order.
-
#seo_description ⇒ Object
Alias for to: :item#seo_description.
-
#seo_keywords ⇒ Object
Alias for to: :item#seo_keywords.
-
#sku ⇒ Object
Alias for to: :item#sku.
-
#spec ⇒ Object
Alias for to: :item#spec.
-
#spec_output ⇒ Object
Alias for to: :item#spec_output.
-
#specifications ⇒ Object
Alias for to: :item#specifications.
-
#specifications_grouped ⇒ Object
Alias for to: :item#specifications_grouped.
-
#tax_class ⇒ Object
Alias for to: :item#tax_class.
-
#tax_rate ⇒ Object
Alias for Catalog#tax_rate.
-
#unit_cogs ⇒ Object
Alias for Store_item#unit_cogs.
-
#upc ⇒ Object
Alias for to: :item#upc.
Methods included from Models::CatalogItemAmazonHelper
#amazon_description, #amazon_feature_1, #amazon_feature_2, #amazon_feature_3, #amazon_feature_4, #amazon_feature_5, #amazon_feature_6, #amazon_feature_7, #amazon_feature_8, #amazon_feature_9, #amazon_generic_keyword, #amazon_target_keywords, #amazon_title, #amazon_variation, #title_for_amazon
Class Method Summary collapse
-
.accessories ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are accessories.
-
.active ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are active.
-
.available_for_edi_feeds ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are available for edi feeds.
-
.available_for_edi_feeds_by_states ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are available for edi feeds by states.
-
.by_skus ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are by skus.
-
.by_upcs ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are by upcs.
- .calculate_discounted_price(price_before_discount, discount, max_discount = 1.0) ⇒ Object
- .catalog_items_for_select ⇒ Object
-
.complimentary_of ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are complimentary of.
-
.controls ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are controls.
- .coupons_for_future_promo_select ⇒ Object
- .coupons_for_promo_select ⇒ Object
-
.discontinued ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are discontinued.
-
.edi_delta_feeds_for_partner_inventory_advice ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are edi delta feeds for partner inventory advice.
-
.edi_delta_feeds_for_partner_price_advice ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are edi delta feeds for partner price advice.
-
.for_edi_feeds ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for edi feeds.
-
.for_edi_feeds_by_states ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for edi feeds by states.
-
.for_google_feed ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for google feed.
-
.for_online_catalog ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for online catalog.
-
.for_online_catalog_or_non_web_accessible_with_successor_item ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for online catalog or non web accessible with successor item.
-
.for_orchestrator_keys ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for orchestrator keys.
-
.for_product_category_path ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for product category path.
-
.for_product_line_path ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for product line path.
-
.for_where_to_buy_list ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for where to buy list.
-
.has_successor_item ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are has successor item.
-
.heating_elements ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are heating elements.
-
.hidden_from_catalog ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are hidden from catalog.
-
.in_stock ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are in stock.
-
.in_stock_with_alternate_warehouse_store_items ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are in stock with alternate warehouse store items.
-
.inactive ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are inactive.
-
.install_kits ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are install kits.
-
.insulations ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are insulations.
-
.integration_kits ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are integration kits.
- .last_custom_mat_catalog_item(catalog_id, volts) ⇒ Object
-
.main_catalogs ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are main catalogs.
-
.non_publications ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are non publications.
-
.non_web_accessible ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are non web accessible.
-
.not_discontinued ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are not discontinued.
-
.not_hidden_from_catalog ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are not hidden from catalog.
-
.not_pending_onboarding ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are not pending onboarding.
-
.order_by_spec ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are order by spec.
-
.ordered_by_sku ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are ordered by sku.
-
.out_of_stock ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are out of stock.
-
.pending_onboarding ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are pending onboarding.
-
.power_modules ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are power modules.
-
.public_catalog_items ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are public catalog items.
-
.relay_panels ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are relay panels.
-
.sensors ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are sensors.
-
.spare_parts ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are spare parts.
- .state_options_for_select(show_all_states = false) ⇒ Object
-
.thermostats ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are thermostats.
-
.towel_warmers ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are towel warmers.
-
.web_accessible ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are web accessible.
-
.with_item ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with item.
-
.with_item_and_classifications ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with item and classifications.
-
.with_item_condition_new ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with item condition new.
-
.with_product_category ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with product category.
-
.with_product_lines ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with product lines.
-
.with_product_specification ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with product specification.
-
.with_third_party_number ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with third party number.
Instance Method Summary collapse
-
#alternate_warehouse_stock_fraction ⇒ Object
Returns the fraction (as decimal) of alternate warehouse stock to report Uses catalog-level setting, defaults to 25% (0.25) This is for EXTERNAL reporting only (feeds, website, EDI).
- #alternate_warehouse_store_items ⇒ Object
- #amazon_variant_catalog_items ⇒ Object
- #amount_currency_symbol ⇒ Object
- #available_in_edi_feed? ⇒ Boolean
- #available_stores ⇒ Object
- #bom_price ⇒ Object
-
#calculate_minimum_price_for_margin(target_margin) ⇒ Object
Provide a target margin below a 100.
- #calculate_profit_margin(price) ⇒ Object
-
#calculate_sale_price(percentage_off, include_vat: false, from_msrp: false) ⇒ Object
Calculates the sale price based on a percentage off (0-100), specify also if price with VAT should be used as the starting amount.
- #calculated_updated_price_from_parent ⇒ Object
- #catalog_discount_human ⇒ Object
- #catalog_items_in_same_catalog(starting_scope = nil) ⇒ Object
- #check_for_price_update ⇒ Object protected
- #check_kit_components_available ⇒ Object protected
- #check_ok_to_destroy ⇒ Object protected
- #check_store_item_is_available ⇒ Object protected
- #check_upc ⇒ Object protected
- #complete_state ⇒ Object
- #content_locales_to_render ⇒ Object
- #create_component_catalog_items ⇒ Object
- #create_single_component_in_catalog_item(store_id, location, component_item) ⇒ Object
- #deep_dup ⇒ Object
-
#dependent_catalog_items(filtered_by_catalog_id = nil) ⇒ Object
Find all similar catalog items in the child catalogs, optionally filter only to one specific catalog.
- #discounted_price(customer = nil, target = nil) ⇒ Object
- #diverging_current_price_vs_proposed_price? ⇒ Boolean
-
#edi_customer_ids_for_catalog ⇒ Object
Returns the list of EDI customer IDs for this catalog, cached for reuse.
-
#edi_orchestrators_for_catalog ⇒ Object
Returns orchestrators for EDI customers in this catalog, cached for reuse Uses the class-level orchestrator cache for fast lookups.
- #edi_partner_sku ⇒ Object
- #effective_seo_description(char_limit: nil) ⇒ Object
- #european? ⇒ Boolean
- #get_successor_online_catalog_item ⇒ Object
- #in_google_feed? ⇒ Boolean
- #in_hide_from_feed_state? ⇒ Boolean
- #in_main_catalog? ⇒ Boolean protected
- #inventory_message_enabled? ⇒ Boolean
-
#inventory_message_processors ⇒ Object
Retrieves all inventory message processors for edi customers carrying this item This can be used to push inventory at once to all channels for one item.
- #is_discontinued? ⇒ Boolean (also: #is_discontinued)
- #item_is_web_accessible? ⇒ Boolean
- #listing_message_enabled? ⇒ Boolean
-
#listing_message_processors ⇒ Object
Retrieves all listing message processors for edi customers carrying this item This can be used to patch listing data at once to all channels for one item.
-
#map_difference ⇒ Object
Difference between retail price and MAP (negative = violation).
-
#map_price ⇒ Object
MAP (Minimum Advertised Price) based on catalog's map_percentage setting Only meaningful for vendor catalogs where retailer controls pricing.
-
#map_to_stores(store_ids) ⇒ Object
This method creates a link between a catalog item and additional stores It is useful for amazon FBA where we keep track of the stock in amazon's warehouses Simply pass an array an store id to map the catalog item to and it will do the rest Note that this method never removes the primary store item for a catalog item.
-
#map_violation? ⇒ Boolean
Check if the retailer's current price is below MAP.
- #missing_kit_components? ⇒ Boolean
- #msrp ⇒ Object
- #msrp_with_vat ⇒ Object
- #name(locale: nil) ⇒ Object
-
#next_available(use_store_item: nil, use_alternate_warehouse: false) ⇒ Object
Returns next available for internal CRM use (100% real stock) discontinued and pending discontinue item will report nil for next available.
- #next_available_by_warehouse(use_alternate_warehouse: false) ⇒ Object
-
#next_available_by_warehouse_with_depth_limit(use_alternate_warehouse: false, max_depth: 10, current_depth: 0) ⇒ Object
Version with depth limit to prevent infinite recursion.
-
#next_available_with_depth_limit(use_store_item: nil, use_alternate_warehouse: false, max_depth: 10, current_depth: 0) ⇒ Object
Version with depth limit to prevent infinite recursion (100% real stock for internal use).
- #notify_of_price_update ⇒ Object protected
- #ok_to_destroy? ⇒ Boolean
- #on_order(use_alternate_warehouse: false) ⇒ Object
-
#on_order_for_store_item(use_store_item: nil, use_alternate_warehouse: false) ⇒ Object
Returns on order quantities for internal CRM use (100% real stock) discontinued and pending discontinue item will report 0 for on order.
- #out_of_stock(use_threshhold = nil) ⇒ Object
- #parent_catalog_currency_symbol ⇒ Object
- #parent_catalog_discount ⇒ Object
-
#parent_catalog_item ⇒ Object
If a parent_catalog exists as defined in the catalog of this catalog item then find the matching catalog item by store item id which should be similar.
- #parent_catalog_item_amount ⇒ Object
- #percentage_off_from_msrp ⇒ Object
- #price_message_enabled? ⇒ Boolean
-
#price_message_processors ⇒ Object
Retrieves all price message processors for edi customers carrying this item This can be used to push price at once to all channels for one item.
-
#price_out_of_date? ⇒ Boolean
This method determines if the catalog item is out of date compared to its parent catalog item, return true/false or nil if not applicable.
- #price_updated? ⇒ Boolean protected
- #primary_catalog ⇒ Object
- #product_category_visible_in_feed? ⇒ Boolean
- #product_stock_status ⇒ Object
- #profit_margin ⇒ Object
- #profit_margin_new_price ⇒ Object
- #profit_margin_new_sale_price ⇒ Object
- #profit_margin_retailer_requested_cost ⇒ Object
- #profit_margin_sale_price ⇒ Object
- #purge_edge_cache ⇒ Object protected
-
#push_inventory_message ⇒ Object
Performs an integration push where applicable for stock to partner carrying this catalog item.
- #push_price_message ⇒ Object
-
#real_stock(use_store_item: nil, use_alternate_warehouse: false) ⇒ Object
Returns REAL stock available (100% of all warehouses) for internal CRM use Use this for cycle counts, pick items, order management, quotes, etc.
- #refresh_google_feed ⇒ Object protected
- #refurbished ⇒ Object
- #reported_name ⇒ Object
- #reported_name_for_google ⇒ Object
-
#reported_stock(use_store_item: nil, use_alternate_warehouse: false, safety_stock: nil) ⇒ Object
Stock for EXTERNAL reporting (feeds, website, EDI) Uses catalog's alternate_warehouse_stock_fraction and reserve_stock discontinued and pending discontinue item will report 0 for stock.
-
#reported_stocks(use_alternate_warehouse: false) ⇒ Object
Returns a hash of store ids and reported stock available in each { 'WarmlyYours-CA': 14, 'WarmlyYours-US': 20 }.
- #reported_vendor_sku(_orchestrator_partner = nil) ⇒ Object
- #reset_edi_delta_feed_fields ⇒ Object
- #retail_price_currency_symbol ⇒ Object
- #root_catalog_item ⇒ Object
- #sale_price_in_effect?(exclude_effective_date: false, date_to_check: nil) ⇒ Boolean
-
#sale_price_percentage_off ⇒ Object
Returns the percentage off (0-100) of an item comparing sale price to catalog price.
-
#sale_price_percentage_off=(percentage_off) ⇒ Object
Assign a percentage off (0-100) and the sale price will be calculated based on the catalog item price without VAT.
-
#sale_price_reset_if_no_coupon ⇒ Object
protected
You cannot have a sale price without a coupon, so before anything, we remove these.
-
#sale_price_with_vat_percentage_off ⇒ Object
Returns the percentage off (0-100) of an item comparing sale price with VAT to catalog price with VAT.
-
#sale_price_with_vat_percentage_off=(percentage_off) ⇒ Object
Assign a percentage off (0-100) and the sale price will be calculated based on the catalog item price with VAT included.
- #seo_title ⇒ Object
- #set_or_clear_discontinued_date ⇒ Object protected
- #should_appear_in_feed? ⇒ Boolean
- #siblings ⇒ Object
- #single_sku? ⇒ Boolean
- #sku_and_name ⇒ Object
- #state_requires_edi_warning_on_order? ⇒ Boolean
- #store_items_by_warehouse ⇒ Object
- #third_party_name_en_required? ⇒ Boolean protected
- #third_party_name_fr_required? ⇒ Boolean protected
- #third_party_part_number_label ⇒ Object
- #third_party_part_number_required? ⇒ Boolean protected
- #third_party_sku_required? ⇒ Boolean protected
- #to_s ⇒ Object
- #tweak_ebay_sku ⇒ Object
- #upc_required? ⇒ Boolean protected
-
#update_price_from_parent ⇒ Object
When the catalog item state moves from pending client update to active, the price is updated.
- #variants(catalog_items_scope: nil, include_self: false, facet_filters: {}) ⇒ Object
Methods included from Models::CatalogItemWayfairHelper
#wayfair_catalog_item?, #wayfair_primary_product_url, #wayfair_product_url, #wayfair_pull_taxonomy_schema, #wayfair_sync_age, #wayfair_synced?, #wayfair_taxonomy_available?, #wayfair_taxonomy_category_id_in_effect, wayfairs
Methods included from Models::CatalogItemWalmartHelper
#walmart_catalog_item?, #walmart_retire_item, walmarts
Methods included from Models::CatalogItemAmazonHelper
#amalytix_tags, #amazon_asin, #amazon_business_price, #amazon_business_price_factor, #amazon_business_price_fba, #amazon_business_price_with_tax, #amazon_business_price_with_tax_fba, #amazon_catalog_item?, #amazon_current_images, #amazon_delete_information, #amazon_effective_business_price_discount, #amazon_effective_desired_product_type, #amazon_item_cost, #amazon_json_generator, #amazon_label_requirements, #amazon_list_price, #amazon_locales, #amazon_lowest_quantity_discount_price, #amazon_lowest_quantity_discounted_price, #amazon_maximum_seller_allowed_price_with_tax, #amazon_maximum_seller_allowed_price_with_tax_fba, #amazon_minimum_profit_margin, #amazon_minimum_seller_allowed_price_with_tax, #amazon_minimum_seller_allowed_price_with_tax_fba, #amazon_patch_information, #amazon_price_with_tax, #amazon_price_with_tax_fba, #amazon_product_data, #amazon_product_type_in_effect, #amazon_product_type_incoherent?, #amazon_product_url, #amazon_pull_buy_box_status, #amazon_pull_catalog_information, #amazon_pull_listing_information, #amazon_pull_listing_schema, #amazon_put_information, #amazon_quantity_discount_price, #amazon_sale_price_with_tax, #amazon_sale_price_with_tax_fba, #amazon_schema, #amazon_seller_item?, #amazon_send_patch_listing_information, #amazon_send_put_listing_information, #amazon_vendor_code, #amazon_vendor_item?, amazons, amazons_sellers, amazons_sellers_with_asins, amazons_with_asins, #amz_available_attributes, #api_ready_state?, #broadcast_amazon_dashboard_update, by_asins, #extract_amazon_procurement_cost_price, #extract_retailer_information, #fba_discount, #get_amz_all_patches, #get_amz_attribute, #get_amz_attributes, #has_amazon_fba?, #max_discount_allowed, #max_discount_allowed_dollars, #max_discount_allowed_percentage, #profit_margin_amazon_minimum_below_target, #profit_marging_amazon_maximum_seller_allowed_price, #profit_marging_amazon_minimum_seller_allowed_price, #ready_to_print_amazon_fba_labels?, #retailer_information_first_locale, with_asin
Methods included from Models::SaleDiscountable
#effective_price, #money_effective_price, #money_price, #money_sale_price
Methods included from Models::Translatable
#compact_translation_container, skip_compaction_for
Methods included from Models::Auditable
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Models::EventPublishable
Instance Attribute Details
#add_kit_components_to_catalog ⇒ Object
Returns the value of attribute add_kit_components_to_catalog.
153 154 155 |
# File 'app/models/catalog_item.rb', line 153 def add_kit_components_to_catalog @add_kit_components_to_catalog end |
#alternate_warehouse_stock_reporting_max ⇒ Object (readonly)
254 |
# File 'app/models/catalog_item.rb', line 254 validates :alternate_warehouse_stock_reporting_max, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true |
#amount ⇒ Object (readonly)
248 |
# File 'app/models/catalog_item.rb', line 248 validates :amount, presence: true, numericality: { greater_than_or_equal_to: 0.0 } |
#item_id ⇒ Object
653 654 655 |
# File 'app/models/catalog_item.rb', line 653 def item_id store_item&.item_id end |
#max_discount ⇒ Object (readonly)
255 |
# File 'app/models/catalog_item.rb', line 255 validates :max_discount, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }, allow_nil: true |
#new_price ⇒ Object (readonly)
246 |
# File 'app/models/catalog_item.rb', line 246 validates :new_price, presence: { if: :new_price_effective_date, message: 'Must be specified alongside new price effective date' } |
#new_price_effective_date ⇒ Object (readonly)
247 |
# File 'app/models/catalog_item.rb', line 247 validates :new_price_effective_date, presence: { if: :new_price, message: 'Must be specified alongside new price' } |
#new_sale_price_with_vat ⇒ Object
Returns the value of attribute new_sale_price_with_vat.
153 154 155 |
# File 'app/models/catalog_item.rb', line 153 def new_sale_price_with_vat @new_sale_price_with_vat end |
#price_with_vat ⇒ Object
Returns the value of attribute price_with_vat.
153 154 155 |
# File 'app/models/catalog_item.rb', line 153 def price_with_vat @price_with_vat end |
#reserve_stock ⇒ Object (readonly)
253 |
# File 'app/models/catalog_item.rb', line 253 validates :reserve_stock, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, allow_nil: true |
#sale_price_with_vat ⇒ Object
Returns the value of attribute sale_price_with_vat.
153 154 155 |
# File 'app/models/catalog_item.rb', line 153 def sale_price_with_vat @sale_price_with_vat end |
#skip_check_kit_components ⇒ Object
Returns the value of attribute skip_check_kit_components.
153 154 155 |
# File 'app/models/catalog_item.rb', line 153 def skip_check_kit_components @skip_check_kit_components end |
#skip_create_kit_components ⇒ Object
Returns the value of attribute skip_create_kit_components.
153 154 155 |
# File 'app/models/catalog_item.rb', line 153 def skip_create_kit_components @skip_create_kit_components end |
#store_ids ⇒ Object
649 650 651 |
# File 'app/models/catalog_item.rb', line 649 def store_ids store_items.map(&:store_id) end |
#third_party_name_en ⇒ Object (readonly)
244 |
# File 'app/models/catalog_item.rb', line 244 validates :third_party_name_en, presence: { if: :third_party_name_en_required? } |
#third_party_name_fr ⇒ Object (readonly)
245 |
# File 'app/models/catalog_item.rb', line 245 validates :third_party_name_fr, presence: { if: :third_party_name_fr_required? } |
#third_party_part_number ⇒ Object (readonly)
validates :store_item_id, uniqueness: { scope: :catalog_id }
Validations:
- Presence ({ if: :third_party_part_number_required? })
242 |
# File 'app/models/catalog_item.rb', line 242 validates :third_party_part_number, presence: { if: :third_party_part_number_required? } |
#third_party_sku ⇒ Object (readonly)
243 |
# File 'app/models/catalog_item.rb', line 243 validates :third_party_sku, presence: { if: :third_party_sku_required? } |
Class Method Details
.accessories ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are accessories. Active Record Scope
329 |
# File 'app/models/catalog_item.rb', line 329 scope :accessories, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_ACCESSORIES)) } |
.active ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are active. Active Record Scope
302 |
# File 'app/models/catalog_item.rb', line 302 scope :active, -> { where(state: %w[active active_hidden]) } |
.available_for_edi_feeds ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are available for edi feeds. Active Record Scope
351 |
# File 'app/models/catalog_item.rb', line 351 scope :available_for_edi_feeds, -> { available_for_edi_feeds_by_states(EDI_FEED_STATUSES) } |
.available_for_edi_feeds_by_states ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are available for edi feeds by states. Active Record Scope
355 356 357 358 |
# File 'app/models/catalog_item.rb', line 355 scope :available_for_edi_feeds_by_states, ->(states) { states_to_use = states.presence || EDI_FEED_STATUSES where(state: states_to_use) } |
.by_skus ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are by skus. Active Record Scope
308 |
# File 'app/models/catalog_item.rb', line 308 scope :by_skus, ->(*skus) { with_item.where(items: { sku: [skus].flatten.uniq.compact }) } |
.by_upcs ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are by upcs. Active Record Scope
309 |
# File 'app/models/catalog_item.rb', line 309 scope :by_upcs, ->(*upcs) { with_item.where(items: { upc: [upcs].flatten.uniq.compact }) } |
.calculate_discounted_price(price_before_discount, discount, max_discount = 1.0) ⇒ Object
484 485 486 487 488 |
# File 'app/models/catalog_item.rb', line 484 def self.calculate_discounted_price(price_before_discount, discount, max_discount = 1.0) price_before_discount ||= 0.0 discount_factor = 1.0 - [max_discount, discount].min (price_before_discount * discount_factor).round(2) end |
.catalog_items_for_select ⇒ Object
474 475 476 |
# File 'app/models/catalog_item.rb', line 474 def self.catalog_items_for_select CatalogItem.includes([:catalog, { store_item: :item }]).order('catalogs.name,items.sku').map { |ci| ["#{ci.sku} - #{ci.name.first(30)} - #{ci.catalog.name}", ci.id] } end |
.complimentary_of ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are complimentary of. Active Record Scope
301 |
# File 'app/models/catalog_item.rb', line 301 scope :complimentary_of, ->(pl) { with_product_lines.where('product_lines.id' => pl.full_complimentary_product_line_ids) } |
.controls ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are controls. Active Record Scope
323 |
# File 'app/models/catalog_item.rb', line 323 scope :controls, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_CONTROLS)) } |
.coupons_for_future_promo_select ⇒ Object
497 498 499 500 501 502 |
# File 'app/models/catalog_item.rb', line 497 def self.coupons_for_future_promo_select coupons = [] coupons += Coupon.active_and_future.tier1.where(calculation_type_goods: 'MX') coupons += Coupon.active_and_future.tier3 coupons.uniq.map { |c| [c.to_s, c.id] }.sort end |
.coupons_for_promo_select ⇒ Object
490 491 492 493 494 495 |
# File 'app/models/catalog_item.rb', line 490 def self.coupons_for_promo_select coupons = [] coupons += Coupon.active.tier1.where(calculation_type_goods: 'MX') coupons += Coupon.active.tier3 coupons.uniq.map { |c| [c.code, c.id] }.sort end |
.discontinued ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are discontinued. Active Record Scope
305 |
# File 'app/models/catalog_item.rb', line 305 scope :discontinued, -> { where(state: %w[discontinued]) } |
.edi_delta_feeds_for_partner_inventory_advice ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are edi delta feeds for partner inventory advice. Active Record Scope
362 363 364 365 366 367 368 369 370 371 372 |
# File 'app/models/catalog_item.rb', line 362 scope :edi_delta_feeds_for_partner_inventory_advice, ->(partner) { # Here we want to a delta query based on store_items.last_inventory_updated_at where(<<-SQL.squish, partner: partner) store_items.last_inventory_updated_at IS NULL OR (catalog_items.last_inventory_advice_sent_json ->> :partner) IS NULL OR ( store_items.last_inventory_updated_at::timestamptz >= (catalog_items.last_inventory_advice_sent_json ->> :partner)::timestamptz ) SQL } |
.edi_delta_feeds_for_partner_price_advice ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are edi delta feeds for partner price advice. Active Record Scope
373 374 375 376 377 378 379 380 381 382 383 |
# File 'app/models/catalog_item.rb', line 373 scope :edi_delta_feeds_for_partner_price_advice, ->(partner) { # Here we want to a delta query based on catalog_items.price_updated_at where(<<-SQL.squish, partner: partner) catalog_items.price_updated_at IS NULL OR (catalog_items.last_price_advice_sent_json ->> :partner) IS NULL OR ( catalog_items.price_updated_at::timestamptz >= (catalog_items.last_price_advice_sent_json ->> :partner)::timestamptz ) SQL } |
.for_edi_feeds ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for edi feeds. Active Record Scope
352 |
# File 'app/models/catalog_item.rb', line 352 scope :for_edi_feeds, -> { available_for_edi_feeds.with_item.ordered_by_sku } |
.for_edi_feeds_by_states ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for edi feeds by states. Active Record Scope
359 360 361 |
# File 'app/models/catalog_item.rb', line 359 scope :for_edi_feeds_by_states, ->(states) { available_for_edi_feeds_by_states(states).with_item.ordered_by_sku } |
.for_google_feed ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for google feed. Active Record Scope
339 |
# File 'app/models/catalog_item.rb', line 339 scope :for_google_feed, -> { for_online_catalog } |
.for_online_catalog ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for online catalog. Active Record Scope
335 336 337 338 |
# File 'app/models/catalog_item.rb', line 335 scope :for_online_catalog, -> { public_catalog_items .merge(Item.goods.non_publications) } |
.for_online_catalog_or_non_web_accessible_with_successor_item ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for online catalog or non web accessible with successor item. Active Record Scope
343 344 345 346 347 348 349 |
# File 'app/models/catalog_item.rb', line 343 scope :for_online_catalog_or_non_web_accessible_with_successor_item, -> { ids = for_online_catalog.pluck(:id) successor_item_ids = non_web_accessible.has_successor_item.pluck(Arel.sql('distinct items.successor_item_id')) successor_item_skus = Item.active.where(id: successor_item_ids).pluck(:sku) successor_item_cis = by_skus(successor_item_skus).where(state: 'active').pluck(:id) where('catalog_items.id IN (?)', (ids + successor_item_cis).uniq) } |
.for_orchestrator_keys ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for orchestrator keys. Active Record Scope
353 |
# File 'app/models/catalog_item.rb', line 353 scope :for_orchestrator_keys, ->(orchestrator_keys) { where(state: ORCHESTRATOR_STATES).with_item.ordered_by_sku.joins(:catalog).merge(Catalog.where(orchestrator_key: orchestrator_keys)) } |
.for_product_category_path ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for product category path. Active Record Scope
318 319 320 |
# File 'app/models/catalog_item.rb', line 318 scope :for_product_category_path, ->(slug_path) { slug_path.blank? ? none : with_item.where(Item[:pc_path_slugs].ltree_descendant(slug_path)) } |
.for_product_line_path ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for product line path. Active Record Scope
315 316 317 |
# File 'app/models/catalog_item.rb', line 315 scope :for_product_line_path, ->(slug_path) { slug_path.blank? ? none : with_item.where(Item[:primary_pl_path_slugs].ltree_descendant(slug_path)) } |
.for_where_to_buy_list ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for where to buy list. Active Record Scope
398 399 400 |
# File 'app/models/catalog_item.rb', line 398 scope :for_where_to_buy_list, ->(parent_catalog_id) { joins(:catalog).where(state: 'active').where.not(catalog_id: [1, 2, 74], url: nil).where(Catalog[:parent_catalog_id].eq(parent_catalog_id)).order(:position, Catalog[:name]).pluck(Arel.sql('COALESCE(catalogs.public_name, catalogs.name), catalog_items.url')) } |
.has_successor_item ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are has successor item. Active Record Scope
342 |
# File 'app/models/catalog_item.rb', line 342 scope :has_successor_item, -> { with_item.where.not(items: { successor_item_id: nil }) } |
.heating_elements ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are heating elements. Active Record Scope
322 |
# File 'app/models/catalog_item.rb', line 322 scope :heating_elements, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_HEATING_ELEMENTS)) } |
.hidden_from_catalog ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are hidden from catalog. Active Record Scope
384 |
# File 'app/models/catalog_item.rb', line 384 scope :hidden_from_catalog, -> { where(state: HIDDEN_STATES) } |
.in_stock ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are in stock. Active Record Scope
388 |
# File 'app/models/catalog_item.rb', line 388 scope :in_stock, -> { with_item.where('store_items.qty_available - COALESCE(catalog_items.reserve_stock, 0) > 0 OR items.always_available_online IS TRUE') } |
.in_stock_with_alternate_warehouse_store_items ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are in stock with alternate warehouse store items. Active Record Scope
389 390 391 392 393 394 |
# File 'app/models/catalog_item.rb', line 389 scope :in_stock_with_alternate_warehouse_store_items, -> { with_item.where("(store_items.qty_available + (select round(coalesce(sum(si.qty_available),0),0) from store_items si inner join stores s on si.store_id = s.id and s.owner = 'warmlyyours' where si.store_id <> store_items.store_id and si.item_id = store_items.item_id and si.location = 'AVAILABLE')) - COALESCE(catalog_items.reserve_stock, 0) > 0 OR items.always_available_online is true") } |
.inactive ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are inactive. Active Record Scope
303 |
# File 'app/models/catalog_item.rb', line 303 scope :inactive, -> { where.not(state: %w[active active_hidden]) } |
.install_kits ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are install kits. Active Record Scope
332 |
# File 'app/models/catalog_item.rb', line 332 scope :install_kits, -> { for_product_line_path(LtreePaths::PL_FLOOR_HEATING_TEMPZONE_INSTALLATION_KITS).merge(accessories) } |
.insulations ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are insulations. Active Record Scope
330 |
# File 'app/models/catalog_item.rb', line 330 scope :insulations, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_ACCESSORIES_INSULATIONS)) } |
.integration_kits ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are integration kits. Active Record Scope
328 |
# File 'app/models/catalog_item.rb', line 328 scope :integration_kits, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_POWER)).where(Item[:primary_pl_path_slugs].ltree_descendant(LtreePaths::PL_FLOOR_HEATING_CONTROL_INTEGRATION)) } |
.last_custom_mat_catalog_item(catalog_id, volts) ⇒ Object
1046 1047 1048 1049 1050 1051 1052 1053 |
# File 'app/models/catalog_item.rb', line 1046 def self.last_custom_mat_catalog_item(catalog_id, volts) for_product_category_path(LtreePaths::PC_HEATING_ELEMENTS) .for_product_line_path(LtreePaths::PL_FLOOR_HEATING_TEMPZONE_CUSTOM_MAT) .where(catalog_id:) .where.not(items: { supplier_item_id: nil }) .where('items.rendered_product_specifications @> ?', { voltage: { raw: volts } }.to_json) .order('items.created_at DESC').first end |
.main_catalogs ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are main catalogs. Active Record Scope
350 |
# File 'app/models/catalog_item.rb', line 350 scope :main_catalogs, -> { where(catalog_id: Catalog.main_catalog_ids) } |
.non_publications ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are non publications. Active Record Scope
296 |
# File 'app/models/catalog_item.rb', line 296 scope :non_publications, -> { with_item.merge(Item.goods.non_publications) } |
.non_web_accessible ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are non web accessible. Active Record Scope
340 |
# File 'app/models/catalog_item.rb', line 340 scope :non_web_accessible, -> { main_catalogs.with_item.merge(Item.goods.non_publications).where('(items.is_discontinued IS TRUE) OR (store_items.is_discontinued IS TRUE) OR (catalog_items.state IN (?))', HIDDEN_STATES) } |
.not_discontinued ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are not discontinued. Active Record Scope
304 |
# File 'app/models/catalog_item.rb', line 304 scope :not_discontinued, -> { where.not(state: %w[discontinued]) } |
.not_hidden_from_catalog ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are not hidden from catalog. Active Record Scope
385 |
# File 'app/models/catalog_item.rb', line 385 scope :not_hidden_from_catalog, -> { where.not(state: HIDDEN_STATES) } |
.not_pending_onboarding ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are not pending onboarding. Active Record Scope
306 |
# File 'app/models/catalog_item.rb', line 306 scope :not_pending_onboarding, -> { where.not(state: %w[pending_onboarding]) } |
.order_by_spec ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are order by spec. Active Record Scope
397 |
# File 'app/models/catalog_item.rb', line 397 scope :order_by_spec, ->(spec, datatype = 'varchar', direction = 'asc') { joins(store_item: :item).merge(Item.order_by_spec(spec, datatype, direction)) } |
.ordered_by_sku ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are ordered by sku. Active Record Scope
298 |
# File 'app/models/catalog_item.rb', line 298 scope :ordered_by_sku, -> { with_item.order(Item[:sku]) } |
.out_of_stock ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are out of stock. Active Record Scope
395 |
# File 'app/models/catalog_item.rb', line 395 scope :out_of_stock, -> { with_item.where('store_items.qty_available - COALESCE(catalog_items.reserve_stock, 0) <= 0 AND items.always_available_online IS NOT TRUE') } |
.pending_onboarding ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are pending onboarding. Active Record Scope
307 |
# File 'app/models/catalog_item.rb', line 307 scope :pending_onboarding, -> { where(state: %w[pending_onboarding]) } |
.power_modules ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are power modules. Active Record Scope
327 |
# File 'app/models/catalog_item.rb', line 327 scope :power_modules, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_POWER_MODULES)) } |
.public_catalog_items ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are public catalog items. Active Record Scope
310 |
# File 'app/models/catalog_item.rb', line 310 scope :public_catalog_items, -> { with_item.merge(StoreItem.active).active.not_hidden_from_catalog } |
.relay_panels ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are relay panels. Active Record Scope
325 |
# File 'app/models/catalog_item.rb', line 325 scope :relay_panels, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_POWER_RELAY_PANELS)) } |
.sensors ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are sensors. Active Record Scope
326 |
# File 'app/models/catalog_item.rb', line 326 scope :sensors, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_SENSORS)) } |
.spare_parts ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are spare parts. Active Record Scope
333 |
# File 'app/models/catalog_item.rb', line 333 scope :spare_parts, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_SPARE_PARTS)) } |
.state_options_for_select(show_all_states = false) ⇒ Object
478 479 480 481 482 |
# File 'app/models/catalog_item.rb', line 478 def self.(show_all_states = false) state_syms = CatalogItem.state_machines[:state].states.map { |s| s.name.to_sym } state_syms -= [:discontinued] unless show_all_states state_syms.map { |s| [s.to_s.humanize, s.to_s] }.sort # discontinued is only for Heatwave bg job end |
.thermostats ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are thermostats. Active Record Scope
324 |
# File 'app/models/catalog_item.rb', line 324 scope :thermostats, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_CONTROLS_THERMOSTATS)) } |
.towel_warmers ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are towel warmers. Active Record Scope
331 |
# File 'app/models/catalog_item.rb', line 331 scope :towel_warmers, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_TOWEL_WARMERS)) } |
.web_accessible ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are web accessible. Active Record Scope
341 |
# File 'app/models/catalog_item.rb', line 341 scope :web_accessible, -> { main_catalogs.with_item.merge(Item.goods.non_publications).where('(items.is_discontinued IS FALSE) AND (store_items.is_discontinued IS FALSE) AND NOT(catalog_items.state IN (?))', HIDDEN_STATES) } |
.with_item ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with item. Active Record Scope
294 |
# File 'app/models/catalog_item.rb', line 294 scope :with_item, -> { joins(store_item: :item) } |
.with_item_and_classifications ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with item and classifications. Active Record Scope
295 |
# File 'app/models/catalog_item.rb', line 295 scope :with_item_and_classifications, -> { joins(store_item: { item: %i[primary_product_line product_category] }).includes(store_item: { item: %i[primary_product_line product_category] }) } |
.with_item_condition_new ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with item condition new. Active Record Scope
297 |
# File 'app/models/catalog_item.rb', line 297 scope :with_item_condition_new, -> { with_item.merge(Item.active.condition_new) } |
.with_product_category ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with product category. Active Record Scope
300 |
# File 'app/models/catalog_item.rb', line 300 scope :with_product_category, -> { includes(store_item: { item: :product_category }).references(store_item: { item: :product_category }) } |
.with_product_lines ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with product lines. Active Record Scope
299 |
# File 'app/models/catalog_item.rb', line 299 scope :with_product_lines, -> { includes(store_item: { item: :product_lines }).references(store_item: { item: :product_lines }) } |
.with_product_specification ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with product specification. Active Record Scope
396 |
# File 'app/models/catalog_item.rb', line 396 scope :with_product_specification, ->(token, value, grouping = nil) { with_item.merge(Item.with_product_specification(token, value, grouping)) } |
.with_third_party_number ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are with third party number. Active Record Scope
334 |
# File 'app/models/catalog_item.rb', line 334 scope :with_third_party_number, -> { where.not(third_party_part_number: [nil, '']) } |
Instance Method Details
#all_amazon_image_profiles ⇒ Object
Alias for
to: :item#all_amazon_image_profiles
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#alternate_warehouse_stock_fraction ⇒ Object
Returns the fraction (as decimal) of alternate warehouse stock to report
Uses catalog-level setting, defaults to 25% (0.25)
This is for EXTERNAL reporting only (feeds, website, EDI)
863 864 865 |
# File 'app/models/catalog_item.rb', line 863 def alternate_warehouse_stock_fraction (catalog&.alternate_warehouse_stock_fraction || 25) / 100.0 end |
#alternate_warehouse_store_items ⇒ Object
980 981 982 |
# File 'app/models/catalog_item.rb', line 980 def alternate_warehouse_store_items StoreItem.where(item_id: store_item.item_id).where.not(store_id: store_item.store_id).available.joins(:store).merge(Store.warmlyyours_warehouses) end |
#amazon_browse_nodes ⇒ ActiveRecord::Relation<AmazonBrowseNode>
180 |
# File 'app/models/catalog_item.rb', line 180 has_and_belongs_to_many :amazon_browse_nodes |
#amazon_catalog_item_flags ⇒ ActiveRecord::Relation<AmazonCatalogItemFlag>
178 |
# File 'app/models/catalog_item.rb', line 178 has_many :amazon_catalog_item_flags, dependent: :destroy |
#amazon_marketplace ⇒ AmazonMarketplace
172 |
# File 'app/models/catalog_item.rb', line 172 has_one :amazon_marketplace, through: :catalog |
#amazon_variant_catalog_items ⇒ Object
1127 1128 1129 1130 1131 |
# File 'app/models/catalog_item.rb', line 1127 def amazon_variant_catalog_items return CatalogItem.none unless item.amazon_variation && item.amazon_variation.items.present? catalog_items_in_same_catalog.merge(item.amazon_variation.items).includes(:item) end |
#amount_currency_symbol ⇒ Object
745 746 747 |
# File 'app/models/catalog_item.rb', line 745 def amount_currency_symbol catalog.currency_symbol end |
#available_in_edi_feed? ⇒ Boolean
657 658 659 |
# File 'app/models/catalog_item.rb', line 657 def available_in_edi_feed? EDI_FEED_STATUSES.include?(state) end |
#available_stores ⇒ Object
645 646 647 |
# File 'app/models/catalog_item.rb', line 645 def available_stores Store.where(company_id: catalog.company_id).order(:name) end |
#bom_price ⇒ Object
737 738 739 |
# File 'app/models/catalog_item.rb', line 737 def bom_price retail_price || amount end |
#calculate_minimum_price_for_margin(target_margin) ⇒ Object
Provide a target margin below a 100
546 547 548 549 550 551 552 553 |
# File 'app/models/catalog_item.rb', line 546 def calculate_minimum_price_for_margin(target_margin) return unless unit_cogs && unit_cogs.positive? && target_margin # skip non-positive unit_cogs raise ArgumentError, 'Target margin must be less than 100%' if target_margin >= 100 minimum_price = unit_cogs / (1 - (target_margin / 100.0)) minimum_price.round(2) # Round to 2 decimal places for currency formatting end |
#calculate_profit_margin(price) ⇒ Object
538 539 540 541 542 543 |
# File 'app/models/catalog_item.rb', line 538 def calculate_profit_margin(price) return unless unit_cogs && price profit = (price - unit_cogs) ((profit / price) * 100).round(2) end |
#calculate_sale_price(percentage_off, include_vat: false, from_msrp: false) ⇒ Object
Calculates the sale price based on a percentage off (0-100), specify also if price with VAT should be used as the starting amount
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 |
# File 'app/models/catalog_item.rb', line 1163 def calculate_sale_price(percentage_off, include_vat: false, from_msrp: false) return nil unless percentage_off.present? && percentage_off.positive? raise 'sale price percentage off must be between 0 and 100' unless (1..100).include?(percentage_off) starting_amount = if from_msrp if include_vat msrp_with_vat else msrp end elsif include_vat price_with_vat else amount end if percentage_off.nil? && starting_amount.nil? msg = "calculate_sale_price: Catalog Item ID #{id} cannot have sale price calculated, percentage off: #{percentage_off}, starting_amount: #{starting_amount}" ErrorReporting.warning(msg) logger.warn msg return nil end calculated_sale_price = (starting_amount * (1.0 - (percentage_off.to_f / 100))).round(2) calculated_sale_price /= (1.0 + tax_rate) if include_vat && tax_rate calculated_sale_price.round(2) end |
#calculated_updated_price_from_parent ⇒ Object
1095 1096 1097 1098 1099 1100 1101 1102 1103 |
# File 'app/models/catalog_item.rb', line 1095 def calculated_updated_price_from_parent return unless (parent_amount = parent_catalog_item&.amount) # Determine discount to use discount = parent_catalog_discount || 0.0 return unless discount.between?(0.0, 1.0) (parent_amount * (1.0 - discount)).round(2) end |
#catalog ⇒ Catalog
166 |
# File 'app/models/catalog_item.rb', line 166 belongs_to :catalog, touch: true, inverse_of: :catalog_items |
#catalog_discount_human ⇒ Object
807 808 809 810 811 |
# File 'app/models/catalog_item.rb', line 807 def catalog_discount_human return unless catalog.parent_catalog_discount "#{(catalog.parent_catalog_discount * 100).round(2)} %" end |
#catalog_items_in_same_catalog(starting_scope = nil) ⇒ Object
1117 1118 1119 1120 1121 1122 1123 1124 1125 |
# File 'app/models/catalog_item.rb', line 1117 def catalog_items_in_same_catalog(starting_scope = nil) starting_scope ||= CatalogItem.active CatalogItem.includes(:catalog) .where(catalog_id:) .with_item .with_product_lines .with_product_category .order(ProductCategory[:priority], CatalogItem[:amount]) end |
#check_for_price_update ⇒ Object (protected)
1292 1293 1294 1295 1296 1297 |
# File 'app/models/catalog_item.rb', line 1292 def check_for_price_update return unless amount_changed? self.price_updated_at = Time.current self.old_amount = amount_was end |
#check_kit_components_available ⇒ Object (protected)
1282 1283 1284 1285 1286 |
# File 'app/models/catalog_item.rb', line 1282 def check_kit_components_available return unless missing_kit_components? errors.add(:base, 'All kit components must be present in the target catalog') end |
#check_ok_to_destroy ⇒ Object (protected)
1288 1289 1290 |
# File 'app/models/catalog_item.rb', line 1288 def check_ok_to_destroy errors.add(:base, 'Cannot destroy because there are dependent line_items, please discontinue instead') unless ok_to_destroy? end |
#check_store_item_is_available ⇒ Object (protected)
1278 1279 1280 |
# File 'app/models/catalog_item.rb', line 1278 def check_store_item_is_available errors.add(:store_item_id, 'Store Item must be part of an available location') if store_item && !store_item.available_location? end |
#check_upc ⇒ Object (protected)
1318 1319 1320 1321 1322 1323 1324 |
# File 'app/models/catalog_item.rb', line 1318 def check_upc # An active public item needs a upc return unless upc_required? && item.upc.blank? item.update_upc = true item.save end |
#complete_state ⇒ Object
733 734 735 |
# File 'app/models/catalog_item.rb', line 733 def complete_state "#{human_state_name} - #{HIDDEN_STATES.include?(state) ? 'not public' : 'public'}" end |
#content_locales_to_render ⇒ Object
1059 1060 1061 |
# File 'app/models/catalog_item.rb', line 1059 def content_locales_to_render (Mobility.available_locales & ([I18n.default_locale] + (catalog.locales || []))).uniq.sort end |
#coupon ⇒ Coupon
167 |
# File 'app/models/catalog_item.rb', line 167 belongs_to :coupon, optional: true |
#create_component_catalog_items ⇒ Object
555 556 557 558 559 560 561 562 |
# File 'app/models/catalog_item.rb', line 555 def create_component_catalog_items store_id = store_item.store_id location = store_item.location item.kit_components.each do |component_item| create_single_component_in_catalog_item(store_id, location, component_item) end true end |
#create_single_component_in_catalog_item(store_id, location, component_item) ⇒ Object
564 565 566 567 568 569 570 571 572 573 574 |
# File 'app/models/catalog_item.rb', line 564 def create_single_component_in_catalog_item(store_id, location, component_item) si = StoreItem.where(item_id: component_item.id, store_id:, location:).first si = StoreItem.create!(item_id: component_item.id, store_id:, location:, qty_on_hand: 0, qty_committed: 0, handling_charge: 0, unit_cogs: 0) if si.nil? existing = CatalogItem.where(catalog_id:, store_item_id: si.id).first return existing if existing.present? # Catalog Item already exists parent_catalog_item = parent_catalog.nil? ? nil : CatalogItem.where(catalog: parent_catalog, store_item: si).first price = parent_catalog_item.nil? ? nil : parent_catalog_item.amount price *= (1 - parent_catalog_discount) if price && parent_catalog_discount CatalogItem.create!(catalog_id:, store_item_id: si.id, amount: price || 0, max_discount: 100, state: 'active_hidden') end |
#currency ⇒ Object
Alias for Catalog#currency
185 186 187 188 189 |
# File 'app/models/catalog_item.rb', line 185 delegate :parent_catalog, :currency, :currency_symbol, :tax_rate, to: :catalog |
#currency_symbol ⇒ Object
Alias for Catalog#currency_symbol
185 186 187 188 189 |
# File 'app/models/catalog_item.rb', line 185 delegate :parent_catalog, :currency, :currency_symbol, :tax_rate, to: :catalog |
#deep_dup ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'app/models/catalog_item.rb', line 135 def deep_dup deep_clone(except: %i[third_party_part_number third_party_promo_part_number third_party_sku new_price new_price_effective_date coupon_id sale_price new_coupon_id new_sale_price price_updated_at parent_sku clearance]) do |_original, copy| copy.clearance = nil end end |
#dependent_catalog_items(filtered_by_catalog_id = nil) ⇒ Object
Find all similar catalog items in the child catalogs, optionally filter only
to one specific catalog
838 839 840 841 842 |
# File 'app/models/catalog_item.rb', line 838 def dependent_catalog_items(filtered_by_catalog_id = nil) catalog.children.select { |c| filtered_by_catalog_id.nil? || c.id == filtered_by_catalog_id }.map do |child_cat| child_cat.catalog_items.where(store_item_id:) end.flatten.uniq end |
#discounted_price(customer = nil, target = nil) ⇒ Object
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 |
# File 'app/models/catalog_item.rb', line 1033 def discounted_price(customer = nil, target = nil) discount = (begin target.pricing_program_discount_factor rescue StandardError nil end) || (begin customer.pricing_program_discount_factor rescue StandardError nil end) CatalogItem.calculate_discounted_price(amount, discount, 1.0) end |
#diverging_current_price_vs_proposed_price? ⇒ Boolean
504 505 506 507 508 509 |
# File 'app/models/catalog_item.rb', line 504 def diverging_current_price_vs_proposed_price? return false unless parent_catalog_discount return false unless (cup = calculated_updated_price_from_parent) amount != cup end |
#edi_communication_logs ⇒ ActiveRecord::Relation<EdiCommunicationLog>
177 |
# File 'app/models/catalog_item.rb', line 177 has_many :edi_communication_logs, through: :edi_documents |
#edi_customer_ids_for_catalog ⇒ Object
Returns the list of EDI customer IDs for this catalog, cached for reuse
674 675 676 |
# File 'app/models/catalog_item.rb', line 674 def edi_customer_ids_for_catalog @edi_customer_ids_for_catalog ||= catalog.customers.where(id: Edi::BaseOrchestrator.customer_ids_edi_enabled).pluck(:id) end |
#edi_documents ⇒ ActiveRecord::Relation<EdiDocument>
176 |
# File 'app/models/catalog_item.rb', line 176 has_many :edi_documents, dependent: :destroy |
#edi_orchestrators_for_catalog ⇒ Object
Returns orchestrators for EDI customers in this catalog, cached for reuse
Uses the class-level orchestrator cache for fast lookups
680 681 682 683 684 |
# File 'app/models/catalog_item.rb', line 680 def edi_orchestrators_for_catalog @edi_orchestrators_for_catalog ||= edi_customer_ids_for_catalog.filter_map do |customer_id| Edi::BaseOrchestrator.orchestrator_for_customer_id(customer_id) end.uniq(&:partner) end |
#edi_partner_sku ⇒ Object
582 583 584 |
# File 'app/models/catalog_item.rb', line 582 def edi_partner_sku third_party_part_number.presence || third_party_sku.presence || sku end |
#effective_seo_description(char_limit: nil) ⇒ Object
727 728 729 730 731 |
# File 'app/models/catalog_item.rb', line 727 def effective_seo_description(char_limit: nil) d = seo_description.presence || item.effective_seo_description(char_limit:) || '' d << " (#{item.sku})" unless Regexp.new(item.sku).match(d) d end |
#european? ⇒ Boolean
856 857 858 |
# File 'app/models/catalog_item.rb', line 856 def european? catalog.country&.eu_country? || false end |
#exported_catalog_item ⇒ ExportedCatalogItem
170 |
# File 'app/models/catalog_item.rb', line 170 has_one :exported_catalog_item, dependent: :delete |
#facet_tokens ⇒ Object
Alias for
to: :item#facet_tokens
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#get_successor_online_catalog_item ⇒ Object
1241 1242 1243 1244 |
# File 'app/models/catalog_item.rb', line 1241 def get_successor_online_catalog_item res = catalog.catalog_items.for_online_catalog.by_skus(item.successor_item.sku).first if !item_is_web_accessible? && item.successor_item.present? res end |
#google_feeds ⇒ ActiveRecord::Relation<GoogleFeed>
175 |
# File 'app/models/catalog_item.rb', line 175 has_many :google_feeds, dependent: :destroy |
#in_google_feed? ⇒ Boolean
1265 1266 1267 |
# File 'app/models/catalog_item.rb', line 1265 def in_google_feed? self.class.for_google_feed.exists?(id: id) end |
#in_hide_from_feed_state? ⇒ Boolean
1137 1138 1139 |
# File 'app/models/catalog_item.rb', line 1137 def in_hide_from_feed_state? HIDDEN_STATES.include?(state) end |
#in_main_catalog? ⇒ Boolean (protected)
1326 1327 1328 |
# File 'app/models/catalog_item.rb', line 1326 def in_main_catalog? Catalog.main_catalog_ids.include?(catalog_id) end |
#inventory_message_enabled? ⇒ Boolean
661 662 663 |
# File 'app/models/catalog_item.rb', line 661 def .present? end |
#inventory_message_processors ⇒ Object
Retrieves all inventory message processors for edi customers carrying this item
This can be used to push inventory at once to all channels for one item
688 689 690 691 692 |
# File 'app/models/catalog_item.rb', line 688 def edi_orchestrators_for_catalog.filter_map do |o| o. if o. && o.respond_to?(:inventory_message_processor) end end |
#is_available_to_public ⇒ Object
Alias for
to: :item#is_available_to_public
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_cable_accessory? ⇒ Object
Alias for
to: :item#is_cable_accessory?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_cable_fit_guide? ⇒ Object
Alias for
to: :item#is_cable_fit_guide?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_circuit_check? ⇒ Object
Alias for
to: :item#is_circuit_check?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_control? ⇒ Object
Alias for
to: :item#is_control?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_discontinued? ⇒ Boolean Also known as: is_discontinued
1246 1247 1248 |
# File 'app/models/catalog_item.rb', line 1246 def is_discontinued? discontinued? || pending_discontinue? end |
#is_heating_element? ⇒ Object
Alias for
to: :item#is_heating_element?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_membrane? ⇒ Object
Alias for
to: :item#is_membrane?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_publication? ⇒ Object
Alias for
to: :item#is_publication?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_roughin_kit? ⇒ Object
Alias for
to: :item#is_roughin_kit?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_snow_melt_plaque? ⇒ Object
Alias for
to: :item#is_snow_melt_plaque?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#is_underlayment? ⇒ Object
Alias for
to: :item#is_underlayment?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#item ⇒ Item
169 |
# File 'app/models/catalog_item.rb', line 169 has_one :item, through: :store_item, inverse_of: :catalog_items |
#item_is_web_accessible? ⇒ Boolean
1235 1236 1237 1238 1239 |
# File 'app/models/catalog_item.rb', line 1235 def item_is_web_accessible? !in_hide_from_feed_state? && !item&.is_discontinued && !store_item&.is_discontinued end |
#line_items ⇒ ActiveRecord::Relation<LineItem>
173 |
# File 'app/models/catalog_item.rb', line 173 has_many :line_items, dependent: :restrict_with_exception |
#listing_message_enabled? ⇒ Boolean
669 670 671 |
# File 'app/models/catalog_item.rb', line 669 def .present? end |
#listing_message_processors ⇒ Object
Retrieves all listing message processors for edi customers carrying this item
This can be used to patch listing data at once to all channels for one item
706 707 708 709 710 |
# File 'app/models/catalog_item.rb', line 706 def edi_orchestrators_for_catalog.filter_map do |o| o. if o.respond_to?(:listing_message_processor) && o. end end |
#map_difference ⇒ Object
Difference between retail price and MAP (negative = violation)
830 831 832 833 834 |
# File 'app/models/catalog_item.rb', line 830 def map_difference return unless retail_price.present? && map_price.present? (retail_price - map_price).round(2) end |
#map_price ⇒ Object
MAP (Minimum Advertised Price) based on catalog's map_percentage setting
Only meaningful for vendor catalogs where retailer controls pricing
815 816 817 818 819 |
# File 'app/models/catalog_item.rb', line 815 def map_price return unless msrp.present? (msrp * catalog.map_percentage).round(2) end |
#map_to_stores(store_ids) ⇒ Object
This method creates a link between a catalog item and additional stores
It is useful for amazon FBA where we keep track of the stock in amazon's warehouses
Simply pass an array an store id to map the catalog item to and it will do the rest
Note that this method never removes the primary store item for a catalog item
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 |
# File 'app/models/catalog_item.rb', line 594 def map_to_stores(store_ids) return unless store_ids.present? store_ids.uniq.each do |sid| s = Store.find(sid) # If store id already exist, nothing to do next if store_items.detect { |si| si.store_id == s.id } # if not quick build if catalog.company_id != s.company_id errors.add(:base, "Store #{s.id} #{s.short_name} does not belong to same company as catalog item #{id}") elsif si = StoreItem.where(store_id: s.id, item_id: item.id, location: 'AVAILABLE').first # See if we already have this si # Then link it store_items << si else # Create a new one (includes implicit linking) store_items.create(store_id: s.id, item_id: item.id, qty_on_hand: 0, qty_committed: 0, unit_cogs: amount, location: 'AVAILABLE') end end # Any stores to remove? store_items.available.each do |si| next if store_item.store_id == si.store_id # The primary store is never removable next if si.store_id.in?(store_ids) # This store is not in the selected list, try to delete it errors.add(:base, "Store Item #{si.id} cannot be deleted due to the presence of stock") unless si.ok_to_destroy? && si.destroy end store_items end |
#map_violation? ⇒ Boolean
Check if the retailer's current price is below MAP
822 823 824 825 826 827 |
# File 'app/models/catalog_item.rb', line 822 def map_violation? return false unless catalog.retailer_type_vendor? return false unless retail_price.present? && map_price.present? retail_price < map_price end |
#missing_kit_components? ⇒ Boolean
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 |
# File 'app/models/catalog_item.rb', line 1213 def missing_kit_components? return false unless item&.is_kit? kit_component_item_skus = item.kit_components.pluck(:sku) kit_components_count = kit_component_item_skus.size catalog_items = CatalogItem.joins(store_item: :item).where(catalog_id:, items: { sku: kit_component_item_skus }, store_items: { location: store_item.location }) catalog_item_skus = catalog_items.pluck(:sku) catalog_items_count = catalog_item_skus.size if kit_components_count == catalog_items_count false else logger.error "Missing kit components in catalog #{catalog_id} for #{item.sku}: #{kit_component_item_skus - catalog_item_skus}" true end end |
#msrp ⇒ Object
791 792 793 |
# File 'app/models/catalog_item.rb', line 791 def msrp root_catalog_item.try(:amount) end |
#msrp_with_vat ⇒ Object
795 796 797 798 799 |
# File 'app/models/catalog_item.rb', line 795 def msrp_with_vat return unless tax_rate.present? root_catalog_item.try(:price_with_vat) end |
#name(locale: nil) ⇒ Object
1064 1065 1066 1067 1068 1069 1070 1071 |
# File 'app/models/catalog_item.rb', line 1064 def name(locale: nil) I18n.with_locale(locale || I18n.locale) do n = third_party_name.presence n ||= item.amazon_title if amazon_catalog_item? n ||= item&.public_name n end end |
#new_coupon ⇒ Coupon
168 |
# File 'app/models/catalog_item.rb', line 168 belongs_to :new_coupon, optional: true, class_name: 'Coupon' |
#next_available(use_store_item: nil, use_alternate_warehouse: false) ⇒ Object
Returns next available for internal CRM use (100% real stock)
discontinued and pending discontinue item will report nil for next available
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 |
# File 'app/models/catalog_item.rb', line 929 def next_available(use_store_item: nil, use_alternate_warehouse: false) return nil if discontinued? || pending_discontinue? use_store_item ||= store_item next_available_hsh_arr = [use_store_item.next_available] if use_alternate_warehouse # What's next available in the other warehouse? Include 100% (real stock) with 1 week delay alternate_warehouse_store_items.each do |alternate_si| alt_next_available_hsh = alternate_si.next_available if alt_next_available_hsh.present? alt_next_available_hsh = OpenStruct.new(next_available_qty: alt_next_available_hsh[:next_available_qty], next_available_date: (alt_next_available_hsh[:next_available_date] + 1.week)).freeze end next_available_hsh_arr << alt_next_available_hsh end end # here we discard all the nil entries and sort by next available date, returning the first entry, i.e. the next available next_available_entry_to_use = next_available_hsh_arr.compact.sort_by { |h| h[:next_available_date] }.first return nil unless next_available_entry_to_use.present? next_available_entry_to_use end |
#next_available_by_warehouse(use_alternate_warehouse: false) ⇒ Object
1002 1003 1004 1005 1006 |
# File 'app/models/catalog_item.rb', line 1002 def next_available_by_warehouse(use_alternate_warehouse: false) store_items_by_warehouse.each_with_object({}) do |si, hsh| hsh[si.store.name] = next_available(use_store_item: si, use_alternate_warehouse:) end end |
#next_available_by_warehouse_with_depth_limit(use_alternate_warehouse: false, max_depth: 10, current_depth: 0) ⇒ Object
Version with depth limit to prevent infinite recursion
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 |
# File 'app/models/catalog_item.rb', line 1009 def next_available_by_warehouse_with_depth_limit(use_alternate_warehouse: false, max_depth: 10, current_depth: 0) # Prevent infinite recursion if current_depth >= max_depth Rails.logger.warn "Maximum depth limit (#{max_depth}) reached for catalog item #{id} (#{item&.sku}). Possible circular reference in kit structure." return {} end store_items_by_warehouse.each_with_object({}) do |si, hsh| hsh[si.store.name] = next_available_with_depth_limit(use_store_item: si, use_alternate_warehouse:, max_depth:, current_depth: current_depth + 1) end end |
#next_available_with_depth_limit(use_store_item: nil, use_alternate_warehouse: false, max_depth: 10, current_depth: 0) ⇒ Object
Version with depth limit to prevent infinite recursion (100% real stock for internal use)
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 |
# File 'app/models/catalog_item.rb', line 953 def next_available_with_depth_limit(use_store_item: nil, use_alternate_warehouse: false, max_depth: 10, current_depth: 0) # Prevent infinite recursion if current_depth >= max_depth Rails.logger.warn "Maximum depth limit (#{max_depth}) reached for catalog item #{id} (#{item&.sku}). Possible circular reference in kit structure." return nil end use_store_item ||= store_item next_available_hsh_arr = [use_store_item.next_available] if use_alternate_warehouse # What's next available in the other warehouse? Include 100% (real stock) with 1 week delay alternate_warehouse_store_items.each do |alternate_si| alt_next_available_hsh = alternate_si.next_available_with_depth_limit(max_depth: max_depth, current_depth: current_depth + 1) if alt_next_available_hsh.present? alt_next_available_hsh = OpenStruct.new(next_available_qty: alt_next_available_hsh[:next_available_qty], next_available_date: (alt_next_available_hsh[:next_available_date] + 1.week)).freeze end next_available_hsh_arr << alt_next_available_hsh end end # here we discard all the nil entries and sort by next available date, returning the first entry, i.e. the next available next_available_entry_to_use = next_available_hsh_arr.compact.sort_by { |h| h[:next_available_date] }.first return nil unless next_available_entry_to_use.present? next_available_entry_to_use end |
#notify_of_price_update ⇒ Object (protected)
1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 |
# File 'app/models/catalog_item.rb', line 1299 def notify_of_price_update Rails.configuration.event_store.publish( Events::PriceUpdated.new(data: { catalog_item_id: id, price_was: old_amount, price_now: amount }), stream_name: "CatalogItem-#{id}" ) end |
#ok_to_destroy? ⇒ Boolean
844 845 846 |
# File 'app/models/catalog_item.rb', line 844 def ok_to_destroy? !line_items.exists? end |
#on_order(use_alternate_warehouse: false) ⇒ Object
996 997 998 999 1000 |
# File 'app/models/catalog_item.rb', line 996 def on_order(use_alternate_warehouse: false) store_items_by_warehouse.each_with_object({}) do |si, hsh| hsh[si.store.name] = on_order_for_store_item(use_store_item: si, use_alternate_warehouse:) end end |
#on_order_for_store_item(use_store_item: nil, use_alternate_warehouse: false) ⇒ Object
Returns on order quantities for internal CRM use (100% real stock)
discontinued and pending discontinue item will report 0 for on order
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 |
# File 'app/models/catalog_item.rb', line 911 def on_order_for_store_item(use_store_item: nil, use_alternate_warehouse: false) return 0 if discontinued? || pending_discontinue? use_store_item ||= store_item on_order_arr = use_store_item.on_order if use_alternate_warehouse # What's on order for the other warehouses? Include 100% (real stock) with 1 week delay alternate_warehouse_store_items.each do |alternate_si| alt_on_order_arr = alternate_si.on_order alt_on_order_arr.each { |h| h[:promised_delivery_date] = (h[:promised_delivery_date] + 1.week) } on_order_arr.concat(alt_on_order_arr) end end on_order_arr end |
#out_of_stock(use_threshhold = nil) ⇒ Object
848 849 850 851 852 853 854 |
# File 'app/models/catalog_item.rb', line 848 def out_of_stock(use_threshhold = nil) # Use catalog_item.reserve_stock instead of legacy item threshold stock_reserved = use_threshhold || reserve_stock.to_i return true if store_item && (store_item.qty_available - stock_reserved) <= 0 false end |
#oversize? ⇒ Object
Alias for
to: :item#oversize?
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#parent_catalog ⇒ Object
Alias for Catalog#parent_catalog
185 186 187 188 189 |
# File 'app/models/catalog_item.rb', line 185 delegate :parent_catalog, :currency, :currency_symbol, :tax_rate, to: :catalog |
#parent_catalog_currency_symbol ⇒ Object
764 765 766 767 768 |
# File 'app/models/catalog_item.rb', line 764 def parent_catalog_currency_symbol return unless parent_catalog parent_catalog.currency_symbol end |
#parent_catalog_discount ⇒ Object
511 512 513 514 515 516 |
# File 'app/models/catalog_item.rb', line 511 def parent_catalog_discount return 0.0 unless catalog.parent_catalog return catalog.parent_catalog_discount_refurb if item.condition_refurbished? catalog.parent_catalog_discount end |
#parent_catalog_item ⇒ Object
If a parent_catalog exists as defined in the catalog of this catalog item
then find the matching catalog item by store item id which should be similar
772 773 774 775 776 |
# File 'app/models/catalog_item.rb', line 772 def parent_catalog_item return unless parent_catalog parent_catalog.catalog_items.where(store_item_id:).first end |
#parent_catalog_item_amount ⇒ Object
778 779 780 781 782 |
# File 'app/models/catalog_item.rb', line 778 def parent_catalog_item_amount return unless pci = parent_catalog_item pci.amount end |
#percentage_off_from_msrp ⇒ Object
801 802 803 804 805 |
# File 'app/models/catalog_item.rb', line 801 def percentage_off_from_msrp return unless msrp && effective_price ((1.0 - (effective_price / msrp)) * 100).round(2) end |
#prefix: :new_sale_price_effective_date ⇒ Object
Alias for New_coupon#effective_date
235 236 237 238 239 |
# File 'app/models/catalog_item.rb', line 235 delegate :effective_date, :expiration_date, to: :new_coupon, prefix: :new_sale_price, allow_nil: true |
#prefix: :new_sale_price_expiration_date ⇒ Object
Alias for New_coupon#expiration_date
235 236 237 238 239 |
# File 'app/models/catalog_item.rb', line 235 delegate :effective_date, :expiration_date, to: :new_coupon, prefix: :new_sale_price, allow_nil: true |
#prefix: :sale_price_effective_date ⇒ Object
Alias for Coupon#effective_date
229 230 231 232 233 |
# File 'app/models/catalog_item.rb', line 229 delegate :effective_date, :expiration_date, to: :coupon, prefix: :sale_price, allow_nil: true |
#prefix: :sale_price_expiration_date ⇒ Object
Alias for Coupon#expiration_date
229 230 231 232 233 |
# File 'app/models/catalog_item.rb', line 229 delegate :effective_date, :expiration_date, to: :coupon, prefix: :sale_price, allow_nil: true |
#price_message_enabled? ⇒ Boolean
665 666 667 |
# File 'app/models/catalog_item.rb', line 665 def .present? end |
#price_message_processors ⇒ Object
Retrieves all price message processors for edi customers carrying this item
This can be used to push price at once to all channels for one item
697 698 699 700 701 |
# File 'app/models/catalog_item.rb', line 697 def edi_orchestrators_for_catalog.filter_map do |o| o. if o. && o.respond_to?(:price_message_processor) end end |
#price_out_of_date? ⇒ Boolean
This method determines if the catalog item is out of date compared to its parent catalog item, return true/false or nil if not applicable.
754 755 756 757 758 759 760 761 762 |
# File 'app/models/catalog_item.rb', line 754 def price_out_of_date? return false unless (pci = parent_catalog_item) # Not applicable if we don't have a parent catalog item return false unless (parent_price_updated_at = pci.price_updated_at) # Not applicable unless the parent catalog item has a price update return false unless catalog.price_sync_timed? # Don't care if not a timed catalog return true unless price_updated_at # Nil value will need to assume an out of date price if parent has one price_age_in_days = parent_price_updated_at - price_updated_at # should be an integer price_age_in_days >= catalog.price_sync_delay # Price is out of date when the catalog price delay is met or exceeded end |
#price_updated? ⇒ Boolean (protected)
1310 1311 1312 |
# File 'app/models/catalog_item.rb', line 1310 def price_updated? saved_changes.key?('amount') end |
#primary_catalog ⇒ Object
586 587 588 |
# File 'app/models/catalog_item.rb', line 586 def primary_catalog catalog_id.in?(CatalogConstants::ALL_MAIN_CATALOG_IDS) end |
#primary_image ⇒ Object
Alias for
to: :item#primary_image
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#primary_product_line ⇒ Object
Alias for
to: :item#primary_product_line
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#primary_product_line_id ⇒ Object
Alias for
to: :item#primary_product_line_id
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#product_category ⇒ Object
Alias for
to: :item#product_category
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#product_category_visible_in_feed? ⇒ Boolean
1145 1146 1147 |
# File 'app/models/catalog_item.rb', line 1145 def product_category_visible_in_feed? product_category&.available_to_public end |
#product_lines ⇒ Object
Alias for
to: :item#product_lines
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#product_stock_status ⇒ Object
1021 1022 1023 1024 1025 1026 1027 |
# File 'app/models/catalog_item.rb', line 1021 def product_stock_status if item.always_available_online? || !out_of_stock 'InStock' else 'OutOfStock' end end |
#profit_margin ⇒ Object
518 519 520 |
# File 'app/models/catalog_item.rb', line 518 def profit_margin calculate_profit_margin amount end |
#profit_margin_new_price ⇒ Object
534 535 536 |
# File 'app/models/catalog_item.rb', line 534 def profit_margin_new_price calculate_profit_margin new_price end |
#profit_margin_new_sale_price ⇒ Object
530 531 532 |
# File 'app/models/catalog_item.rb', line 530 def profit_margin_new_sale_price calculate_profit_margin new_sale_price end |
#profit_margin_retailer_requested_cost ⇒ Object
522 523 524 |
# File 'app/models/catalog_item.rb', line 522 def profit_margin_retailer_requested_cost calculate_profit_margin retailer_requested_cost end |
#profit_margin_sale_price ⇒ Object
526 527 528 |
# File 'app/models/catalog_item.rb', line 526 def profit_margin_sale_price calculate_profit_margin sale_price end |
#public_description_html ⇒ Object
Alias for
to: :item#public_description_html
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#public_name ⇒ Object
Alias for
to: :item#public_name
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#purge_edge_cache ⇒ Object (protected)
1360 1361 1362 1363 1364 1365 1366 |
# File 'app/models/catalog_item.rb', line 1360 def purge_edge_cache return unless saved_changes? # only need to do this for main catalogs return unless Catalog.main_catalog_ids.include?(catalog_id) site_maps.each(&:purge_edge_cache) end |
#push_inventory_message ⇒ Object
Performs an integration push where applicable for stock to partner carrying this catalog item
714 715 716 |
# File 'app/models/catalog_item.rb', line 714 def .map { |imp| imp.process(catalog_items: CatalogItem.where(id:)) }.flatten end |
#push_price_message ⇒ Object
718 719 720 721 |
# File 'app/models/catalog_item.rb', line 718 def logger.warn "No price message processors found for catalog item #{id}" if .empty? .map { |imp| imp.process(catalog_items: CatalogItem.where(id:)) }.flatten end |
#qty_available ⇒ Object
Alias for Store_item#qty_available
224 225 226 227 |
# File 'app/models/catalog_item.rb', line 224 delegate :qty_available, :qty_available_outside_order, :unit_cogs, to: :store_item |
#qty_available_outside_order ⇒ Object
Alias for Store_item#qty_available_outside_order
224 225 226 227 |
# File 'app/models/catalog_item.rb', line 224 delegate :qty_available, :qty_available_outside_order, :unit_cogs, to: :store_item |
#real_stock(use_store_item: nil, use_alternate_warehouse: false) ⇒ Object
Returns REAL stock available (100% of all warehouses) for internal CRM use
Use this for cycle counts, pick items, order management, quotes, etc.
869 870 871 872 873 874 875 876 877 878 |
# File 'app/models/catalog_item.rb', line 869 def real_stock(use_store_item: nil, use_alternate_warehouse: false) use_store_item ||= store_item qty_available = use_store_item.qty_available.to_i if use_alternate_warehouse alternate_warehouse_store_items.each do |alternate_si| qty_available += alternate_si.qty_available.to_i end end qty_available end |
#refresh_google_feed ⇒ Object (protected)
1314 1315 1316 |
# File 'app/models/catalog_item.rb', line 1314 def refresh_google_feed Feed::Google::ListGenerator.new(catalog_item_ids: [id]) end |
#refurbished ⇒ Object
1229 1230 1231 1232 1233 |
# File 'app/models/catalog_item.rb', line 1229 def refurbished return unless irv = item.refurbished_version irv.catalog_items.where(catalog_id:).not_hidden_from_catalog.first end |
#reported_name ⇒ Object
1077 1078 1079 1080 |
# File 'app/models/catalog_item.rb', line 1077 def reported_name # name.encode("ASCII", :invalid => :replace, :undef => :replace, :replace => "") name end |
#reported_name_for_google ⇒ Object
1082 1083 1084 |
# File 'app/models/catalog_item.rb', line 1082 def reported_name_for_google google_feed_title.presence || reported_name end |
#reported_stock(use_store_item: nil, use_alternate_warehouse: false, safety_stock: nil) ⇒ Object
Stock for EXTERNAL reporting (feeds, website, EDI)
Uses catalog's alternate_warehouse_stock_fraction and reserve_stock
discontinued and pending discontinue item will report 0 for stock
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 |
# File 'app/models/catalog_item.rb', line 883 def reported_stock(use_store_item: nil, use_alternate_warehouse: false, safety_stock: nil) return 0 if discontinued? || pending_discontinue? safety_stock ||= reserve_stock.to_i use_store_item ||= store_item qty_available = use_store_item.qty_available.to_i if use_alternate_warehouse # What's in the other warehouse? We report a fraction (default 25%) of that stock fraction = alternate_warehouse_stock_fraction alternate_warehouse_store_items.each do |alternate_si| alternate_stock = (alternate_si.qty_available * fraction).ceil # If alternate_warehouse_stock_reporting_max is set, cap the alternate stock at that value alternate_stock = [alternate_stock, alternate_warehouse_stock_reporting_max].min if alternate_warehouse_stock_reporting_max.present? qty_available += alternate_stock end end # if we have a min reported stock (safety_stock) set we will subtract it first, 2 avail with min of 2 to report will report 0 qty_available = [qty_available - safety_stock, 0].max # If our always available stock is set we will use that value if the qty available is below if use_store_item.permanent_qty_available&.zero? 0 else [qty_available, min_stock_to_report, use_store_item.permanent_qty_available].compact.max end end |
#reported_stocks(use_alternate_warehouse: false) ⇒ Object
Returns a hash of store ids and reported stock available in each
{ 'WarmlyYours-CA': 14, 'WarmlyYours-US': 20 }
990 991 992 993 994 |
# File 'app/models/catalog_item.rb', line 990 def reported_stocks(use_alternate_warehouse: false) store_items_by_warehouse.each_with_object({}) do |si, hsh| hsh[si.store.name] = reported_stock(use_store_item: si, use_alternate_warehouse:) end end |
#reported_vendor_sku(_orchestrator_partner = nil) ⇒ Object
1073 1074 1075 |
# File 'app/models/catalog_item.rb', line 1073 def reported_vendor_sku(_orchestrator_partner = nil) third_party_sku.presence || sku end |
#reset_edi_delta_feed_fields ⇒ Object
1256 1257 1258 1259 1260 1261 1262 1263 |
# File 'app/models/catalog_item.rb', line 1256 def reset_edi_delta_feed_fields EDI_DELTA_FEED_CATEGORIES.each do |category| = :"last_#{category}_sent_json" if respond_to?() && try().present? # if this column exists in CatalogItem, then let's reset it, which ensures it will get sent in the next schedule EDI feed for that category of EDI message update_column(, {}) end end end |
#retail_price_currency_symbol ⇒ Object
749 750 751 |
# File 'app/models/catalog_item.rb', line 749 def retail_price_currency_symbol catalog.currency_symbol end |
#retailer_probes ⇒ ActiveRecord::Relation<CatalogItemRetailerProbe>
179 |
# File 'app/models/catalog_item.rb', line 179 has_many :retailer_probes, class_name: 'CatalogItemRetailerProbe', dependent: :destroy |
#root_catalog_item ⇒ Object
784 785 786 787 788 789 |
# File 'app/models/catalog_item.rb', line 784 def root_catalog_item root_catalog = catalog.root return self unless root_catalog != catalog root_catalog.catalog_items.find_by(store_item_id:) end |
#sale_price_in_effect?(exclude_effective_date: false, date_to_check: nil) ⇒ Boolean
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 |
# File 'app/models/catalog_item.rb', line 1149 def sale_price_in_effect?(exclude_effective_date: false, date_to_check: nil) return false unless sale_price.present? unless exclude_effective_date date_to_check ||= Date.current # If today is before the sale start, don't send return false if sale_price_effective_date && date_to_check < sale_price_effective_date # If today is past the sale expiration, don't send return false if sale_price_expiration_date && date_to_check > sale_price_expiration_date end true end |
#sale_price_percentage_off ⇒ Object
Returns the percentage off (0-100) of an item comparing sale price to catalog price
1195 1196 1197 1198 1199 |
# File 'app/models/catalog_item.rb', line 1195 def sale_price_percentage_off return 0.0 unless sale_price.present? && amount.present? && amount.positive? ((1.0 - (sale_price / amount)) * 100).round(2) end |
#sale_price_percentage_off=(percentage_off) ⇒ Object
Assign a percentage off (0-100) and the sale price will be calculated based on the catalog item price without VAT
1190 1191 1192 |
# File 'app/models/catalog_item.rb', line 1190 def sale_price_percentage_off=(percentage_off) calculate_sale_price(percentage_off) end |
#sale_price_reset_if_no_coupon ⇒ Object (protected)
You cannot have a sale price without a coupon, so before anything, we remove these
1272 1273 1274 1275 1276 |
# File 'app/models/catalog_item.rb', line 1272 def sale_price_reset_if_no_coupon return if coupon.present? self.sale_price = nil end |
#sale_price_with_vat_percentage_off ⇒ Object
Returns the percentage off (0-100) of an item comparing sale price with VAT to catalog price with VAT
1207 1208 1209 1210 1211 |
# File 'app/models/catalog_item.rb', line 1207 def sale_price_with_vat_percentage_off return 0.0 unless sale_price_with_vat.present? ((1.0 - (sale_price_with_vat / price_with_vat)) * 100).round(2) end |
#sale_price_with_vat_percentage_off=(percentage_off) ⇒ Object
Assign a percentage off (0-100) and the sale price will be calculated based on the catalog item price with VAT included
1202 1203 1204 |
# File 'app/models/catalog_item.rb', line 1202 def sale_price_with_vat_percentage_off=(percentage_off) calculate_sale_price(percentage_off, include_vat: true) end |
#seo_description ⇒ Object
Alias for
to: :item#seo_description
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#seo_keywords ⇒ Object
Alias for
to: :item#seo_keywords
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#seo_title ⇒ Object
723 724 725 |
# File 'app/models/catalog_item.rb', line 723 def seo_title item.effective_seo_title end |
#set_or_clear_discontinued_date ⇒ Object (protected)
1350 1351 1352 1353 1354 1355 1356 1357 1358 |
# File 'app/models/catalog_item.rb', line 1350 def set_or_clear_discontinued_date return unless state_changed? if %w[pending_discontinue discontinued].include?(state) && %w[active active_hidden pending_onboarding pending_vendor_update invalid_catalog_item].include?(state_was) self.discontinued_date = Date.current elsif %w[pending_discontinue discontinued].include?(state_was) && %w[active active_hidden pending_onboarding pending_vendor_update invalid_catalog_item].include?(state) self.discontinued_date = nil end end |
#should_appear_in_feed? ⇒ Boolean
1141 1142 1143 |
# File 'app/models/catalog_item.rb', line 1141 def should_appear_in_feed? !in_hide_from_feed_state? && product_category_visible_in_feed? end |
#siblings ⇒ Object
1106 1107 1108 |
# File 'app/models/catalog_item.rb', line 1106 def siblings CatalogItem.where(catalog_id:) end |
#single_sku? ⇒ Boolean
1133 1134 1135 |
# File 'app/models/catalog_item.rb', line 1133 def single_sku? variants(include_self: true).size <= 1 end |
#site_maps ⇒ ActiveRecord::Relation<SiteMap>
174 |
# File 'app/models/catalog_item.rb', line 174 has_many :site_maps, as: :resource, dependent: :destroy |
#sku ⇒ Object
Alias for
to: :item#sku
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#sku_and_name ⇒ Object
1029 1030 1031 |
# File 'app/models/catalog_item.rb', line 1029 def sku_and_name item.sku + ' - ' + item.name end |
#spec ⇒ Object
Alias for
to: :item#spec
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#spec_output ⇒ Object
Alias for
to: :item#spec_output
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#specifications ⇒ Object
Alias for
to: :item#specifications
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#specifications_grouped ⇒ Object
Alias for
to: :item#specifications_grouped
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#state_requires_edi_warning_on_order? ⇒ Boolean
1251 1252 1253 1254 |
# File 'app/models/catalog_item.rb', line 1251 def state_requires_edi_warning_on_order? # here we warn when this item is in a state that should not be ordered via EDI, but allow order to go through %w[active require_vendor_update].include?(state) != true end |
#store ⇒ Store
171 |
# File 'app/models/catalog_item.rb', line 171 has_one :store, through: :store_item |
#store_item ⇒ StoreItem
165 |
# File 'app/models/catalog_item.rb', line 165 belongs_to :store_item, inverse_of: :catalog_items |
#store_items ⇒ ActiveRecord::Relation<StoreItem>
181 |
# File 'app/models/catalog_item.rb', line 181 has_and_belongs_to_many :store_items, inverse_of: :catalog_items |
#store_items_by_warehouse ⇒ Object
984 985 986 |
# File 'app/models/catalog_item.rb', line 984 def store_items_by_warehouse store_items.joins(:store).includes(:store).merge(Store.warmlyyours_warehouses) end |
#tax_class ⇒ Object
Alias for
to: :item#tax_class
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#tax_rate ⇒ Object
Alias for Catalog#tax_rate
185 186 187 188 189 |
# File 'app/models/catalog_item.rb', line 185 delegate :parent_catalog, :currency, :currency_symbol, :tax_rate, to: :catalog |
#third_party_name_en_required? ⇒ Boolean (protected)
1342 1343 1344 |
# File 'app/models/catalog_item.rb', line 1342 def third_party_name_en_required? active? && catalog.third_party_name_en_required end |
#third_party_name_fr_required? ⇒ Boolean (protected)
1346 1347 1348 |
# File 'app/models/catalog_item.rb', line 1346 def third_party_name_fr_required? active? && catalog.third_party_name_fr_required end |
#third_party_part_number_label ⇒ Object
1055 1056 1057 |
# File 'app/models/catalog_item.rb', line 1055 def third_party_part_number_label catalog.determine_retailer_part_number_label end |
#third_party_part_number_required? ⇒ Boolean (protected)
1334 1335 1336 |
# File 'app/models/catalog_item.rb', line 1334 def third_party_part_number_required? active? && catalog.third_party_part_number_required end |
#third_party_sku_required? ⇒ Boolean (protected)
1338 1339 1340 |
# File 'app/models/catalog_item.rb', line 1338 def third_party_sku_required? active? && catalog.third_party_sku_required end |
#to_s ⇒ Object
741 742 743 |
# File 'app/models/catalog_item.rb', line 741 def to_s "CatalogItem[#{id}]" end |
#tweak_ebay_sku ⇒ Object
576 577 578 579 580 |
# File 'app/models/catalog_item.rb', line 576 def tweak_ebay_sku return unless catalog.orchestrator_key == 'ebay_us' update(third_party_sku: item.sku.tr('.', '*')) end |
#unit_cogs ⇒ Object
Alias for Store_item#unit_cogs
224 225 226 227 |
# File 'app/models/catalog_item.rb', line 224 delegate :qty_available, :qty_available_outside_order, :unit_cogs, to: :store_item |
#upc ⇒ Object
Alias for
to: :item#upc
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/catalog_item.rb', line 191 delegate :sku, :upc, :public_name, :primary_product_line, :primary_product_line_id, :product_category, :product_lines, :is_available_to_public, :oversize?, :is_circuit_check?, :is_underlayment?, :is_snow_melt_plaque?, :is_roughin_kit?, :is_cable_fit_guide?, :is_cable_accessory?, :is_membrane?, :is_heating_element?, :is_publication?, :is_control?, :primary_image, :seo_keywords, :seo_description, :public_description_html, :facet_tokens, :spec, :spec_output, :specifications, :specifications_grouped, :tax_class, :all_amazon_image_profiles, allow_nil: true, to: :item |
#upc_required? ⇒ Boolean (protected)
1330 1331 1332 |
# File 'app/models/catalog_item.rb', line 1330 def upc_required? available_in_edi_feed? && catalog.require_upc? end |
#update_price_from_parent ⇒ Object
When the catalog item state moves from pending client update to active, the price is updated
1087 1088 1089 1090 1091 1092 1093 |
# File 'app/models/catalog_item.rb', line 1087 def update_price_from_parent return unless parent_catalog_item push_price_service = Catalog::PushCatalogItemPrice.new result = push_price_service.process(parent_catalog_item, target_catalog_id: catalog_id) result.all_price_pushed? end |
#variants(catalog_items_scope: nil, include_self: false, facet_filters: {}) ⇒ Object
1110 1111 1112 1113 1114 1115 |
# File 'app/models/catalog_item.rb', line 1110 def variants(catalog_items_scope: nil, include_self: false, facet_filters: {}) item_variants = item.item_grouping_info(include_self:, facet_filters:)&.variants return CatalogItem.none if item_variants.blank? catalog_items_in_same_catalog(CatalogItem.for_online_catalog).merge(item_variants).includes(:item) end |