Class: CatalogItem
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- CatalogItem
- Includes:
- Memery, Models::Auditable, Models::CatalogItemAmazonHelper, Models::CatalogItemWalmartHelper, Models::CatalogItemWayfairHelper, Models::SaleDiscountable, Models::Translatable, 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
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_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_... (coupon_id => coupons.id)
fk_rails_... (new_coupon_id => coupons.id) ON DELETE => nullify
Defined Under Namespace
Classes: CouponAndSalePriceValidator, Onboarder, PriceSync, PriceUpdatedHandler, QuantityBoundsValidator, Remapper, ScheduledPriceChanger
Constant Summary collapse
- HIDDEN_STATES =
Recognised hidden states.
%w[active_hidden discontinued pending_client_review invalid_catalog_item pending_onboarding].freeze
- EDI_FEED_STATUSES =
Recognised 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].freeze
- ORCHESTRATOR_STATES =
Recognised orchestrator states.
%w[active require_vendor_update pending_vendor_update pending_discontinue discontinued].freeze
Constants included from Models::CatalogItemWalmartHelper
Models::CatalogItemWalmartHelper::WALMART_ALREADY_RETIRED_ERROR_CODE
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
Constants included from Schedulable
Schedulable::SIMPLE_FORM_OPTIONS
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
- #reserve_stock ⇒ Object readonly
-
#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
-
#third_party_sku_variant_id ⇒ Object
readonly
Marketplace variant selector (e.g. Wayfair piid): a given variant must map to at most one of our catalog items per catalog.
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>
- #listing_issues ⇒ ActiveRecord::Relation<ListingIssue>
- #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.
-
.primary_catalogs_first ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are primary catalogs first.
-
.primary_catalogs_first_then_by_state_and_name ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are primary catalogs first then by state and name.
-
.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_offer_new_sale_price ⇒ Object
-
#edi_offer_price ⇒ Object
EDI offer prices.
- #edi_offer_sale_price ⇒ Object
-
#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
- #new_sale_price_with_vat ⇒ 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
- #price_with_vat ⇒ Object
- #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 ⇒ Object
-
#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.
-
#sellable_online_qty(use_store_item: nil) ⇒ Integer
Units actually offerable to shoppers for THIS catalog: the primary warehouse's available stock minus the reserve buffer, never negative.
- #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 Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Instance Attribute Details
#add_kit_components_to_catalog ⇒ Object
Returns the value of attribute add_kit_components_to_catalog.
156 157 158 |
# File 'app/models/catalog_item.rb', line 156 def add_kit_components_to_catalog @add_kit_components_to_catalog end |
#alternate_warehouse_stock_reporting_max ⇒ Object (readonly)
261 |
# File 'app/models/catalog_item.rb', line 261 validates :alternate_warehouse_stock_reporting_max, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true |
#amount ⇒ Object (readonly)
255 |
# File 'app/models/catalog_item.rb', line 255 validates :amount, presence: true, numericality: { greater_than_or_equal_to: 0.0 } |
#item_id ⇒ Object
676 677 678 |
# File 'app/models/catalog_item.rb', line 676 def item_id store_item&.item_id end |
#max_discount ⇒ Object (readonly)
262 |
# File 'app/models/catalog_item.rb', line 262 validates :max_discount, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }, allow_nil: true |
#new_price ⇒ Object (readonly)
253 |
# File 'app/models/catalog_item.rb', line 253 validates :new_price, presence: { if: :new_price_effective_date, message: 'Must be specified alongside new price effective date' } |
#new_price_effective_date ⇒ Object (readonly)
254 |
# File 'app/models/catalog_item.rb', line 254 validates :new_price_effective_date, presence: { if: :new_price, message: 'Must be specified alongside new price' } |
#reserve_stock ⇒ Object (readonly)
260 |
# File 'app/models/catalog_item.rb', line 260 validates :reserve_stock, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, allow_nil: true |
#skip_check_kit_components ⇒ Object
Returns the value of attribute skip_check_kit_components.
156 157 158 |
# File 'app/models/catalog_item.rb', line 156 def skip_check_kit_components @skip_check_kit_components end |
#skip_create_kit_components ⇒ Object
Returns the value of attribute skip_create_kit_components.
156 157 158 |
# File 'app/models/catalog_item.rb', line 156 def skip_create_kit_components @skip_create_kit_components end |
#store_ids ⇒ Object
672 673 674 |
# File 'app/models/catalog_item.rb', line 672 def store_ids store_items.map(&:store_id) end |
#third_party_name_en ⇒ Object (readonly)
251 |
# File 'app/models/catalog_item.rb', line 251 validates :third_party_name_en, presence: { if: :third_party_name_en_required? } |
#third_party_name_fr ⇒ Object (readonly)
252 |
# File 'app/models/catalog_item.rb', line 252 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? })
243 |
# File 'app/models/catalog_item.rb', line 243 validates :third_party_part_number, presence: { if: :third_party_part_number_required? } |
#third_party_sku ⇒ Object (readonly)
244 |
# File 'app/models/catalog_item.rb', line 244 validates :third_party_sku, presence: { if: :third_party_sku_required? } |
#third_party_sku_variant_id ⇒ Object (readonly)
Marketplace variant selector (e.g. Wayfair piid): a given variant must map to
at most one of our catalog items per catalog. allow_nil so the many single /
non-variant items (no selector) coexist — a blank selector is normalized to
nil (see the normalizes block below) so a form-submitted "" doesn't trip
this against another blank item. The partial DB index is the backstop.
Validations:
- Uniqueness ({ scope: :catalog_id })
- Allow_nil
250 |
# File 'app/models/catalog_item.rb', line 250 validates :third_party_sku_variant_id, uniqueness: { scope: :catalog_id }, allow_nil: true |
Class Method Details
.accessories ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are accessories. Active Record Scope
324 |
# File 'app/models/catalog_item.rb', line 324 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
297 |
# File 'app/models/catalog_item.rb', line 297 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
346 |
# File 'app/models/catalog_item.rb', line 346 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
350 351 352 353 |
# File 'app/models/catalog_item.rb', line 350 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
303 |
# File 'app/models/catalog_item.rb', line 303 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
304 |
# File 'app/models/catalog_item.rb', line 304 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
486 487 488 489 490 |
# File 'app/models/catalog_item.rb', line 486 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
476 477 478 |
# File 'app/models/catalog_item.rb', line 476 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
296 |
# File 'app/models/catalog_item.rb', line 296 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
318 |
# File 'app/models/catalog_item.rb', line 318 scope :controls, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_CONTROLS)) } |
.coupons_for_future_promo_select ⇒ Object
499 500 501 502 503 504 |
# File 'app/models/catalog_item.rb', line 499 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
492 493 494 495 496 497 |
# File 'app/models/catalog_item.rb', line 492 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
300 |
# File 'app/models/catalog_item.rb', line 300 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
355 356 357 358 359 360 361 362 363 364 365 |
# File 'app/models/catalog_item.rb', line 355 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
366 367 368 369 370 371 372 373 374 375 376 |
# File 'app/models/catalog_item.rb', line 366 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
347 |
# File 'app/models/catalog_item.rb', line 347 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
354 |
# File 'app/models/catalog_item.rb', line 354 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
334 |
# File 'app/models/catalog_item.rb', line 334 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
330 331 332 333 |
# File 'app/models/catalog_item.rb', line 330 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
338 339 340 341 342 343 344 |
# File 'app/models/catalog_item.rb', line 338 scope :for_online_catalog_or_non_web_accessible_with_successor_item, -> { ids = for_online_catalog.ids 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').ids where(catalog_items: { id: (ids + successor_item_cis).uniq }) } |
.for_orchestrator_keys ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are for orchestrator keys. Active Record Scope
348 |
# File 'app/models/catalog_item.rb', line 348 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
313 314 315 |
# File 'app/models/catalog_item.rb', line 313 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
310 311 312 |
# File 'app/models/catalog_item.rb', line 310 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
391 392 393 |
# File 'app/models/catalog_item.rb', line 391 scope :for_where_to_buy_list, ->(parent_catalog_id) { joins(:catalog).where(state: 'active').where.not(catalog_id: [1, 2, 74]).where.not(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
337 |
# File 'app/models/catalog_item.rb', line 337 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
317 |
# File 'app/models/catalog_item.rb', line 317 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
377 |
# File 'app/models/catalog_item.rb', line 377 scope :hidden_from_catalog, -> { where(state: HIDDEN_STATES) } |
.in_stock ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are in stock. Active Record Scope
381 |
# File 'app/models/catalog_item.rb', line 381 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
382 383 384 385 386 387 |
# File 'app/models/catalog_item.rb', line 382 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
298 |
# File 'app/models/catalog_item.rb', line 298 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
327 |
# File 'app/models/catalog_item.rb', line 327 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
325 |
# File 'app/models/catalog_item.rb', line 325 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
323 |
# File 'app/models/catalog_item.rb', line 323 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
1086 1087 1088 1089 1090 1091 1092 1093 |
# File 'app/models/catalog_item.rb', line 1086 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
345 |
# File 'app/models/catalog_item.rb', line 345 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
291 |
# File 'app/models/catalog_item.rb', line 291 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
335 |
# File 'app/models/catalog_item.rb', line 335 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
299 |
# File 'app/models/catalog_item.rb', line 299 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
378 |
# File 'app/models/catalog_item.rb', line 378 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
301 |
# File 'app/models/catalog_item.rb', line 301 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
390 |
# File 'app/models/catalog_item.rb', line 390 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
293 |
# File 'app/models/catalog_item.rb', line 293 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
388 |
# File 'app/models/catalog_item.rb', line 388 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
302 |
# File 'app/models/catalog_item.rb', line 302 scope :pending_onboarding, -> { where(state: %w[pending_onboarding]) } |
.power_modules ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are power modules. Active Record Scope
322 |
# File 'app/models/catalog_item.rb', line 322 scope :power_modules, -> { with_item.where(Item[:pc_path_slugs].ltree_descendant(LtreePaths::PC_POWER_MODULES)) } |
.primary_catalogs_first ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are primary catalogs first. Active Record Scope
279 280 281 282 |
# File 'app/models/catalog_item.rb', line 279 scope :primary_catalogs_first, -> { in_order_of(:catalog_id, CatalogConstants::ALL_MAIN_CATALOG_IDS, filter: false) .order(Arel.sql('catalogs.name ASC')) } |
.primary_catalogs_first_then_by_state_and_name ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are primary catalogs first then by state and name. Active Record Scope
283 284 285 286 287 |
# File 'app/models/catalog_item.rb', line 283 scope :primary_catalogs_first_then_by_state_and_name, -> { in_order_of(:catalog_id, CatalogConstants::ALL_MAIN_CATALOG_IDS, filter: false) .in_order_of(:state, %w[active pending_client_review pending_onboarding require_vendor_update pending_vendor_update pending_discontinue active_hidden discontinued invalid_catalog_item], filter: false) .order(Arel.sql('catalogs.name ASC')) } |
.public_catalog_items ⇒ ActiveRecord::Relation<CatalogItem>
A relation of CatalogItems that are public catalog items. Active Record Scope
305 |
# File 'app/models/catalog_item.rb', line 305 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
320 |
# File 'app/models/catalog_item.rb', line 320 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
321 |
# File 'app/models/catalog_item.rb', line 321 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
328 |
# File 'app/models/catalog_item.rb', line 328 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
480 481 482 483 484 |
# File 'app/models/catalog_item.rb', line 480 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
319 |
# File 'app/models/catalog_item.rb', line 319 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
326 |
# File 'app/models/catalog_item.rb', line 326 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
336 |
# File 'app/models/catalog_item.rb', line 336 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
289 |
# File 'app/models/catalog_item.rb', line 289 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
290 |
# File 'app/models/catalog_item.rb', line 290 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
292 |
# File 'app/models/catalog_item.rb', line 292 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
295 |
# File 'app/models/catalog_item.rb', line 295 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
294 |
# File 'app/models/catalog_item.rb', line 294 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
389 |
# File 'app/models/catalog_item.rb', line 389 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
329 |
# File 'app/models/catalog_item.rb', line 329 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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)
903 904 905 |
# File 'app/models/catalog_item.rb', line 903 def alternate_warehouse_stock_fraction (catalog&.alternate_warehouse_stock_fraction || 25) / 100.0 end |
#alternate_warehouse_store_items ⇒ Object
1020 1021 1022 |
# File 'app/models/catalog_item.rb', line 1020 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>
181 |
# File 'app/models/catalog_item.rb', line 181 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
1167 1168 1169 1170 1171 |
# File 'app/models/catalog_item.rb', line 1167 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
768 769 770 |
# File 'app/models/catalog_item.rb', line 768 def amount_currency_symbol catalog.currency_symbol end |
#available_in_edi_feed? ⇒ Boolean
680 681 682 |
# File 'app/models/catalog_item.rb', line 680 def available_in_edi_feed? EDI_FEED_STATUSES.include?(state) end |
#available_stores ⇒ Object
668 669 670 |
# File 'app/models/catalog_item.rb', line 668 def available_stores Store.where(company_id: catalog.company_id).order(:name) end |
#bom_price ⇒ Object
760 761 762 |
# File 'app/models/catalog_item.rb', line 760 def bom_price retail_price || amount end |
#calculate_minimum_price_for_margin(target_margin) ⇒ Object
Provide a target margin below a 100
548 549 550 551 552 553 554 555 |
# File 'app/models/catalog_item.rb', line 548 def calculate_minimum_price_for_margin(target_margin) return unless 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
540 541 542 543 544 545 |
# File 'app/models/catalog_item.rb', line 540 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
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 |
# File 'app/models/catalog_item.rb', line 1203 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
1135 1136 1137 1138 1139 1140 1141 1142 1143 |
# File 'app/models/catalog_item.rb', line 1135 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
830 831 832 833 834 |
# File 'app/models/catalog_item.rb', line 830 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
1157 1158 1159 1160 1161 1162 1163 1164 1165 |
# File 'app/models/catalog_item.rb', line 1157 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)
1332 1333 1334 1335 1336 1337 |
# File 'app/models/catalog_item.rb', line 1332 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)
1322 1323 1324 1325 1326 |
# File 'app/models/catalog_item.rb', line 1322 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)
1328 1329 1330 |
# File 'app/models/catalog_item.rb', line 1328 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)
1318 1319 1320 |
# File 'app/models/catalog_item.rb', line 1318 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)
1358 1359 1360 1361 1362 1363 1364 |
# File 'app/models/catalog_item.rb', line 1358 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
756 757 758 |
# File 'app/models/catalog_item.rb', line 756 def complete_state "#{human_state_name} - #{HIDDEN_STATES.include?(state) ? 'not public' : 'public'}" end |
#content_locales_to_render ⇒ Object
1099 1100 1101 |
# File 'app/models/catalog_item.rb', line 1099 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
557 558 559 560 561 562 563 564 |
# File 'app/models/catalog_item.rb', line 557 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
566 567 568 569 570 571 572 573 574 575 576 |
# File 'app/models/catalog_item.rb', line 566 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&.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
186 187 188 189 190 |
# File 'app/models/catalog_item.rb', line 186 delegate :parent_catalog, :currency, :currency_symbol, :tax_rate, to: :catalog |
#currency_symbol ⇒ Object
Alias for Catalog#currency_symbol
186 187 188 189 190 |
# File 'app/models/catalog_item.rb', line 186 delegate :parent_catalog, :currency, :currency_symbol, :tax_rate, to: :catalog |
#deep_dup ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'app/models/catalog_item.rb', line 136 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| # clearance is NOT NULL with a false default; reset the clone to false # rather than nil, which would violate the constraint on save. copy.clearance = false 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
861 862 863 864 865 |
# File 'app/models/catalog_item.rb', line 861 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
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 |
# File 'app/models/catalog_item.rb', line 1073 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
506 507 508 509 510 511 |
# File 'app/models/catalog_item.rb', line 506 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
697 698 699 |
# File 'app/models/catalog_item.rb', line 697 def edi_customer_ids_for_catalog @edi_customer_ids_for_catalog ||= catalog.customers.where(id: Edi::BaseOrchestrator.customer_ids_edi_enabled).ids end |
#edi_documents ⇒ ActiveRecord::Relation<EdiDocument>
176 |
# File 'app/models/catalog_item.rb', line 176 has_many :edi_documents, dependent: :destroy |
#edi_offer_new_sale_price ⇒ Object
662 663 664 665 666 |
# File 'app/models/catalog_item.rb', line 662 def edi_offer_new_sale_price return new_sale_price_with_vat if tax_rate.present? new_sale_price.to_f.positive? ? new_sale_price : nil end |
#edi_offer_price ⇒ Object
EDI offer prices. European Mirakl partners carry a catalog tax_rate and
list VAT-inclusive, so the *_with_vat helpers apply. Tax-exclusive
markets (e.g. Best Buy Canada — CAD, no VAT) have a nil tax_rate, which
makes those helpers return nil; for those, fall back to the plain pre-tax
price/sale so the offer feed isn't sent priceless.
652 653 654 |
# File 'app/models/catalog_item.rb', line 652 def edi_offer_price tax_rate.present? ? price_with_vat : amount end |
#edi_offer_sale_price ⇒ Object
656 657 658 659 660 |
# File 'app/models/catalog_item.rb', line 656 def edi_offer_sale_price return sale_price_with_vat if tax_rate.present? sale_price.to_f.positive? ? sale_price : nil end |
#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
703 704 705 706 707 |
# File 'app/models/catalog_item.rb', line 703 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
584 585 586 |
# File 'app/models/catalog_item.rb', line 584 def edi_partner_sku third_party_part_number.presence || third_party_sku.presence || sku end |
#effective_seo_description(char_limit: nil) ⇒ Object
750 751 752 753 754 |
# File 'app/models/catalog_item.rb', line 750 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
896 897 898 |
# File 'app/models/catalog_item.rb', line 896 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
1281 1282 1283 1284 |
# File 'app/models/catalog_item.rb', line 1281 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
1305 1306 1307 |
# File 'app/models/catalog_item.rb', line 1305 def in_google_feed? self.class.for_google_feed.exists?(id: id) end |
#in_hide_from_feed_state? ⇒ Boolean
1177 1178 1179 |
# File 'app/models/catalog_item.rb', line 1177 def in_hide_from_feed_state? HIDDEN_STATES.include?(state) end |
#in_main_catalog? ⇒ Boolean (protected)
1366 1367 1368 |
# File 'app/models/catalog_item.rb', line 1366 def in_main_catalog? Catalog.main_catalog_ids.include?(catalog_id) end |
#inventory_message_enabled? ⇒ Boolean
684 685 686 |
# File 'app/models/catalog_item.rb', line 684 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
711 712 713 714 715 |
# File 'app/models/catalog_item.rb', line 711 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
1286 1287 1288 |
# File 'app/models/catalog_item.rb', line 1286 def is_discontinued? discontinued? || pending_discontinue? end |
#is_heating_element? ⇒ Object
Alias for
to: :item#is_heating_element?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
1275 1276 1277 1278 1279 |
# File 'app/models/catalog_item.rb', line 1275 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_issues ⇒ ActiveRecord::Relation<ListingIssue>
179 |
# File 'app/models/catalog_item.rb', line 179 has_many :listing_issues, dependent: :destroy |
#listing_message_enabled? ⇒ Boolean
692 693 694 |
# File 'app/models/catalog_item.rb', line 692 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
729 730 731 732 733 |
# File 'app/models/catalog_item.rb', line 729 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)
853 854 855 856 857 |
# File 'app/models/catalog_item.rb', line 853 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
838 839 840 841 842 |
# File 'app/models/catalog_item.rb', line 838 def map_price return if msrp.blank? (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
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 622 623 |
# File 'app/models/catalog_item.rb', line 596 def map_to_stores(store_ids) return if store_ids.blank? store_ids.uniq.each do |sid| s = Store.find(sid) # If store id already exist, nothing to do next if store_items.find { |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
845 846 847 848 849 850 |
# File 'app/models/catalog_item.rb', line 845 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
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 |
# File 'app/models/catalog_item.rb', line 1253 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
814 815 816 |
# File 'app/models/catalog_item.rb', line 814 def msrp root_catalog_item.try(:amount) end |
#msrp_with_vat ⇒ Object
818 819 820 821 822 |
# File 'app/models/catalog_item.rb', line 818 def msrp_with_vat return if tax_rate.blank? root_catalog_item.try(:price_with_vat) end |
#name(locale: nil) ⇒ Object
1104 1105 1106 1107 1108 1109 1110 1111 |
# File 'app/models/catalog_item.rb', line 1104 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' |
#new_sale_price_with_vat ⇒ Object
639 640 641 642 643 644 645 |
# File 'app/models/catalog_item.rb', line 639 def new_sale_price_with_vat return if tax_rate.blank? res = ((tax_rate + 1) * (new_sale_price || 0.0)).round(2) res = nil if res == 0.0 res end |
#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
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 |
# File 'app/models/catalog_item.rb', line 969 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.min_by { |h| h[:next_available_date] } return nil if next_available_entry_to_use.blank? next_available_entry_to_use end |
#next_available_by_warehouse(use_alternate_warehouse: false) ⇒ Object
1042 1043 1044 1045 1046 |
# File 'app/models/catalog_item.rb', line 1042 def next_available_by_warehouse(use_alternate_warehouse: false) store_items_by_warehouse.to_h do |si| [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
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 |
# File 'app/models/catalog_item.rb', line 1049 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.to_h do |si| [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)
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 |
# File 'app/models/catalog_item.rb', line 993 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.min_by { |h| h[:next_available_date] } return nil if next_available_entry_to_use.blank? next_available_entry_to_use end |
#notify_of_price_update ⇒ Object (protected)
1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 |
# File 'app/models/catalog_item.rb', line 1339 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
867 868 869 |
# File 'app/models/catalog_item.rb', line 867 def ok_to_destroy? !line_items.exists? end |
#on_order(use_alternate_warehouse: false) ⇒ Object
1036 1037 1038 1039 1040 |
# File 'app/models/catalog_item.rb', line 1036 def on_order(use_alternate_warehouse: false) store_items_by_warehouse.to_h do |si| [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
951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 |
# File 'app/models/catalog_item.rb', line 951 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
871 872 873 874 875 876 877 |
# File 'app/models/catalog_item.rb', line 871 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?
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
186 187 188 189 190 |
# File 'app/models/catalog_item.rb', line 186 delegate :parent_catalog, :currency, :currency_symbol, :tax_rate, to: :catalog |
#parent_catalog_currency_symbol ⇒ Object
787 788 789 790 791 |
# File 'app/models/catalog_item.rb', line 787 def parent_catalog_currency_symbol return unless parent_catalog parent_catalog.currency_symbol end |
#parent_catalog_discount ⇒ Object
513 514 515 516 517 518 |
# File 'app/models/catalog_item.rb', line 513 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
795 796 797 798 799 |
# File 'app/models/catalog_item.rb', line 795 def parent_catalog_item return unless parent_catalog parent_catalog.catalog_items.where(store_item_id:).first end |
#parent_catalog_item_amount ⇒ Object
801 802 803 804 805 |
# File 'app/models/catalog_item.rb', line 801 def parent_catalog_item_amount return unless (pci = parent_catalog_item) pci.amount end |
#percentage_off_from_msrp ⇒ Object
824 825 826 827 828 |
# File 'app/models/catalog_item.rb', line 824 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
236 237 238 239 240 |
# File 'app/models/catalog_item.rb', line 236 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
236 237 238 239 240 |
# File 'app/models/catalog_item.rb', line 236 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
230 231 232 233 234 |
# File 'app/models/catalog_item.rb', line 230 delegate :effective_date, :expiration_date, to: :coupon, prefix: :sale_price, allow_nil: true |
#prefix: :sale_price_expiration_date ⇒ Object
Alias for Coupon#expiration_date
230 231 232 233 234 |
# File 'app/models/catalog_item.rb', line 230 delegate :effective_date, :expiration_date, to: :coupon, prefix: :sale_price, allow_nil: true |
#price_message_enabled? ⇒ Boolean
688 689 690 |
# File 'app/models/catalog_item.rb', line 688 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
720 721 722 723 724 |
# File 'app/models/catalog_item.rb', line 720 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.
777 778 779 780 781 782 783 784 785 |
# File 'app/models/catalog_item.rb', line 777 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)
1350 1351 1352 |
# File 'app/models/catalog_item.rb', line 1350 def price_updated? saved_changes.key?('amount') end |
#price_with_vat ⇒ Object
625 626 627 628 629 |
# File 'app/models/catalog_item.rb', line 625 def price_with_vat return if tax_rate.blank? ((tax_rate + 1) * (amount || 0.0)).round(2) end |
#primary_catalog ⇒ Object
588 589 590 |
# File 'app/models/catalog_item.rb', line 588 def primary_catalog catalog_id.in?(CatalogConstants::ALL_MAIN_CATALOG_IDS) end |
#primary_image ⇒ Object
Alias for
to: :item#primary_image
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
1185 1186 1187 |
# File 'app/models/catalog_item.rb', line 1185 def product_category_visible_in_feed? product_category&.available_to_public end |
#product_lines ⇒ Object
Alias for
to: :item#product_lines
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
1061 1062 1063 1064 1065 1066 1067 |
# File 'app/models/catalog_item.rb', line 1061 def product_stock_status if item.always_available_online? || !out_of_stock 'InStock' else 'OutOfStock' end end |
#profit_margin ⇒ Object
520 521 522 |
# File 'app/models/catalog_item.rb', line 520 def profit_margin calculate_profit_margin amount end |
#profit_margin_new_price ⇒ Object
536 537 538 |
# File 'app/models/catalog_item.rb', line 536 def profit_margin_new_price calculate_profit_margin new_price end |
#profit_margin_new_sale_price ⇒ Object
532 533 534 |
# File 'app/models/catalog_item.rb', line 532 def profit_margin_new_sale_price calculate_profit_margin new_sale_price end |
#profit_margin_retailer_requested_cost ⇒ Object
524 525 526 |
# File 'app/models/catalog_item.rb', line 524 def profit_margin_retailer_requested_cost calculate_profit_margin retailer_requested_cost end |
#profit_margin_sale_price ⇒ Object
528 529 530 |
# File 'app/models/catalog_item.rb', line 528 def profit_margin_sale_price calculate_profit_margin sale_price end |
#public_description_html ⇒ Object
Alias for
to: :item#public_description_html
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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)
1400 1401 1402 1403 1404 1405 1406 |
# File 'app/models/catalog_item.rb', line 1400 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
737 738 739 |
# File 'app/models/catalog_item.rb', line 737 def .map { |imp| imp.process(catalog_items: CatalogItem.where(id:)) }.flatten end |
#push_price_message ⇒ Object
741 742 743 744 |
# File 'app/models/catalog_item.rb', line 741 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
225 226 227 228 |
# File 'app/models/catalog_item.rb', line 225 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
225 226 227 228 |
# File 'app/models/catalog_item.rb', line 225 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.
909 910 911 912 913 914 915 916 917 918 |
# File 'app/models/catalog_item.rb', line 909 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)
1354 1355 1356 |
# File 'app/models/catalog_item.rb', line 1354 def refresh_google_feed Feed::Google::ListGenerator.new(catalog_item_ids: [id]) end |
#refurbished ⇒ Object
1269 1270 1271 1272 1273 |
# File 'app/models/catalog_item.rb', line 1269 def refurbished return unless (irv = item.refurbished_version) irv.catalog_items.where(catalog_id:).not_hidden_from_catalog.first end |
#reported_name ⇒ Object
1117 1118 1119 1120 |
# File 'app/models/catalog_item.rb', line 1117 def reported_name # name.encode("ASCII", :invalid => :replace, :undef => :replace, :replace => "") name end |
#reported_name_for_google ⇒ Object
1122 1123 1124 |
# File 'app/models/catalog_item.rb', line 1122 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
923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 |
# File 'app/models/catalog_item.rb', line 923 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 }
1030 1031 1032 1033 1034 |
# File 'app/models/catalog_item.rb', line 1030 def reported_stocks(use_alternate_warehouse: false) store_items_by_warehouse.to_h do |si| [si.store.name, reported_stock(use_store_item: si, use_alternate_warehouse:)] end end |
#reported_vendor_sku(_orchestrator_partner = nil) ⇒ Object
1113 1114 1115 |
# File 'app/models/catalog_item.rb', line 1113 def reported_vendor_sku(_orchestrator_partner = nil) third_party_sku.presence || sku end |
#reset_edi_delta_feed_fields ⇒ Object
1296 1297 1298 1299 1300 1301 1302 1303 |
# File 'app/models/catalog_item.rb', line 1296 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
772 773 774 |
# File 'app/models/catalog_item.rb', line 772 def retail_price_currency_symbol catalog.currency_symbol end |
#retailer_probes ⇒ ActiveRecord::Relation<CatalogItemRetailerProbe>
180 |
# File 'app/models/catalog_item.rb', line 180 has_many :retailer_probes, class_name: 'CatalogItemRetailerProbe', dependent: :destroy |
#root_catalog_item ⇒ Object
807 808 809 810 811 812 |
# File 'app/models/catalog_item.rb', line 807 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
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 |
# File 'app/models/catalog_item.rb', line 1189 def sale_price_in_effect?(exclude_effective_date: false, date_to_check: nil) return false if sale_price.blank? 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
1235 1236 1237 1238 1239 |
# File 'app/models/catalog_item.rb', line 1235 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
1230 1231 1232 |
# File 'app/models/catalog_item.rb', line 1230 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
1312 1313 1314 1315 1316 |
# File 'app/models/catalog_item.rb', line 1312 def sale_price_reset_if_no_coupon return if coupon.present? self.sale_price = nil end |
#sale_price_with_vat ⇒ Object
631 632 633 634 635 636 637 |
# File 'app/models/catalog_item.rb', line 631 def sale_price_with_vat return if tax_rate.blank? res = ((tax_rate + 1) * (sale_price || 0.0)).round(2) res = nil if res == 0.0 res 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
1247 1248 1249 1250 1251 |
# File 'app/models/catalog_item.rb', line 1247 def sale_price_with_vat_percentage_off return 0.0 if sale_price_with_vat.blank? ((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
1242 1243 1244 |
# File 'app/models/catalog_item.rb', line 1242 def sale_price_with_vat_percentage_off=(percentage_off) calculate_sale_price(percentage_off, include_vat: true) end |
#sellable_online_qty(use_store_item: nil) ⇒ Integer
Units actually offerable to shoppers for THIS catalog: the primary
warehouse's available stock minus the reserve buffer, never negative.
This is the reserve math behind #out_of_stock and therefore
#product_stock_status — the In / Out of Stock status shoppers see (which
additionally honors the item's always_available_online override).
Alternate-warehouse stock is deliberately NOT counted here — it only
contributes to #reported_stock for external feeds.
889 890 891 892 893 894 |
# File 'app/models/catalog_item.rb', line 889 def sellable_online_qty(use_store_item: nil) use_store_item ||= store_item return 0 unless use_store_item [use_store_item.qty_available.to_i - reserve_stock.to_i, 0].max end |
#seo_description ⇒ Object
Alias for
to: :item#seo_description
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
746 747 748 |
# File 'app/models/catalog_item.rb', line 746 def seo_title item.effective_seo_title end |
#set_or_clear_discontinued_date ⇒ Object (protected)
1390 1391 1392 1393 1394 1395 1396 1397 1398 |
# File 'app/models/catalog_item.rb', line 1390 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
1181 1182 1183 |
# File 'app/models/catalog_item.rb', line 1181 def should_appear_in_feed? !in_hide_from_feed_state? && product_category_visible_in_feed? end |
#siblings ⇒ Object
1146 1147 1148 |
# File 'app/models/catalog_item.rb', line 1146 def siblings CatalogItem.where(catalog_id:) end |
#single_sku? ⇒ Boolean
1173 1174 1175 |
# File 'app/models/catalog_item.rb', line 1173 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
1069 1070 1071 |
# File 'app/models/catalog_item.rb', line 1069 def sku_and_name "#{item.sku} - #{item.name}" end |
#spec ⇒ Object
Alias for
to: :item#spec
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
1291 1292 1293 1294 |
# File 'app/models/catalog_item.rb', line 1291 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>
182 |
# File 'app/models/catalog_item.rb', line 182 has_and_belongs_to_many :store_items, inverse_of: :catalog_items |
#store_items_by_warehouse ⇒ Object
1024 1025 1026 |
# File 'app/models/catalog_item.rb', line 1024 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
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 223 |
# File 'app/models/catalog_item.rb', line 192 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
186 187 188 189 190 |
# File 'app/models/catalog_item.rb', line 186 delegate :parent_catalog, :currency, :currency_symbol, :tax_rate, to: :catalog |
#third_party_name_en_required? ⇒ Boolean (protected)
1382 1383 1384 |
# File 'app/models/catalog_item.rb', line 1382 def third_party_name_en_required? active? && catalog.third_party_name_en_required end |
#third_party_name_fr_required? ⇒ Boolean (protected)
1386 1387 1388 |
# File 'app/models/catalog_item.rb', line 1386 def third_party_name_fr_required? active? && catalog.third_party_name_fr_required end |
#third_party_part_number_label ⇒ Object
1095 1096 1097 |
# File 'app/models/catalog_item.rb', line 1095 def third_party_part_number_label catalog.determine_retailer_part_number_label end |
#third_party_part_number_required? ⇒ Boolean (protected)
1374 1375 1376 |
# File 'app/models/catalog_item.rb', line 1374 def third_party_part_number_required? active? && catalog.third_party_part_number_required end |
#third_party_sku_required? ⇒ Boolean (protected)
1378 1379 1380 |
# File 'app/models/catalog_item.rb', line 1378 def third_party_sku_required? active? && catalog.third_party_sku_required end |
#to_s ⇒ Object
764 765 766 |
# File 'app/models/catalog_item.rb', line 764 def to_s "CatalogItem[#{id}]" end |
#tweak_ebay_sku ⇒ Object
578 579 580 581 582 |
# File 'app/models/catalog_item.rb', line 578 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
225 226 227 228 |
# File 'app/models/catalog_item.rb', line 225 delegate :qty_available, :qty_available_outside_order, :unit_cogs, to: :store_item |
#upc ⇒ Object
Alias for
to: :item#upc
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 223 |
# File 'app/models/catalog_item.rb', line 192 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)
1370 1371 1372 |
# File 'app/models/catalog_item.rb', line 1370 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
1127 1128 1129 1130 1131 1132 1133 |
# File 'app/models/catalog_item.rb', line 1127 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
1150 1151 1152 1153 1154 1155 |
# File 'app/models/catalog_item.rb', line 1150 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 |