Class: ItemsController
- Inherits:
-
CrmController
- Object
- ActionController::Base
- ApplicationController
- CrmController
- ItemsController
- Defined in:
- app/controllers/items_controller.rb
Overview
Controller: items.
Constant Summary collapse
- TAB_ACTIONS =
Tab fragments never render standalone — non-frame hits redirect to show ?tab=.
%i[tab_alerts tab_main_attributes tab_descriptions tab_classifications tab_specifications tab_logistics tab_store_items tab_catalog_items tab_revisions tab_images tab_all_images tab_videos tab_floor_plans tab_reviews tab_faqs tab_publications tab_suppliers tab_kit_contents tab_kits tab_amazon tab_relatives].freeze
Constants included from Controllers::ReferenceFindable
Controllers::ReferenceFindable::ID_EMBEDDED_PATTERNS
Constants included from Controllers::AnalyticsEvents
Controllers::AnalyticsEvents::MAX_QUEUED_EVENTS, Controllers::AnalyticsEvents::SESSION_KEY
Constants included from Controllers::ErrorRendering
Controllers::ErrorRendering::NON_CONTENT_PATH_PREFIXES
Instance Method Summary collapse
-
#apply_template_specs ⇒ void
Creates the item's template specifications and refreshes the rendered specifications.
-
#auto_translate_attributes ⇒ void
Queues (or runs inline in development) attribute translation for the item.
-
#auto_translate_product_specifications ⇒ void
Queues (or runs inline in development) product-specification translation for the item.
-
#clone ⇒ void
Renders the clone/revise form for an item, offering refurbish when no refurbished version exists yet.
-
#consolidate_kit ⇒ void
Consolidates all kit fields from the kit's component items.
-
#copy_related_items_from_siblings ⇒ void
Copies related items from the item's siblings, optionally restricted to
params[:relation_type]. -
#create ⇒ Object
POST /items POST /items.xml.
-
#do_clone ⇒ void
Performs the clone/revise/refurbish operation via
Item::Clonerand redirects to the new item on success. -
#edit ⇒ Object
GET /items/1/edit.
-
#edit_amazon ⇒ void
Renders the Amazon-specific editor for an item.
-
#edit_catalogs ⇒ void
Renders the catalog-assignment editor for an item.
-
#edit_kit_contents ⇒ Object
Dedicated editor for kit contents only.
-
#export_amalytics_snapshot ⇒ void
Exports an Amalytix XLSX snapshot of the item's Amazon seller catalog items and streams the file.
-
#feature_update ⇒ void
Updates an item inline from the feature grid and responds with best-in-place JSON.
-
#features ⇒ void
Renders the feature grid of items, defaulting to items public and active in the main catalogs.
-
#flush_cache ⇒ void
Touches the item and queues an edge-cache purge for it.
-
#identifier_label ⇒ void
Streams a UPC/item-identifier PDF label, honoring
upcparams for size, Amazon Transparency output, serial number, and target market. -
#import_amazon_images ⇒ Object
POST /items/:id/import_amazon_images Imports missing Amazon images from live catalog data into our image library.
-
#index ⇒ void
Renders the item search/list page.
-
#item_label ⇒ void
Streams a storage-location PDF label for the given item ids.
-
#lookup ⇒ void
Returns JSON autocomplete results for item lookup, honoring scope, kit, exclusion, and condition filters.
-
#make_primary_image ⇒ void
Makes the image given by
params[:image_id]the item's primary image. -
#new ⇒ Object
GET /items/new GET /items/new.xml.
-
#print_identifier_label ⇒ void
Prints one or more identifier labels via a
PrintProfile, or stashes the combined PDF for download when no print profile is selected. -
#print_label ⇒ void
Renders the printable label view for an item.
-
#prune_empty_specs ⇒ void
Removes empty template specifications from the item and refreshes the rendered specifications.
-
#publication ⇒ void
Redirects to the authenticated URL of a publication item.
-
#push_amazon_listing_data_all ⇒ Object
POST /items/:id/push_amazon_listing_data_all Pushes listing data to all active Amazon catalog items for this item.
-
#refresh_embedding ⇒ void
Regenerates the item's search embedding, using chunked embeddings for publications.
-
#refresh_rendered_product_specifications ⇒ void
Rebuilds the item's rendered product specifications, optionally limited to the given
params[:tokens]. -
#show ⇒ void
Shows a single item, found by id or SKU, redirecting publication items to the publication view unless
force_item_viewis set. -
#tab_alerts ⇒ Object
Tab actions for turbo-tabs lazy loading.
-
#tab_all_images ⇒ void
Renders the all-images tab, collecting every image related to the item with optional tag and locale filters.
-
#tab_amazon ⇒ void
Renders the Amazon tab fragment for an item.
-
#tab_catalog_items ⇒ void
Renders the catalog-items tab fragment for an item.
-
#tab_classifications ⇒ void
Renders the classifications tab fragment for an item.
-
#tab_descriptions ⇒ void
Renders the descriptions tab fragment for an item.
-
#tab_faqs ⇒ void
Renders the FAQs tab fragment for an item.
-
#tab_floor_plans ⇒ void
Renders the floor-plans tab fragment for an item.
-
#tab_images ⇒ void
Renders the images tab fragment for an item.
-
#tab_kit_contents ⇒ void
Renders the kit-contents tab fragment, loading the kit's component item relations.
-
#tab_kits ⇒ void
Renders the kits tab fragment, listing the kits this item belongs to.
-
#tab_logistics ⇒ void
Renders the logistics tab fragment for an item.
-
#tab_main_attributes ⇒ void
Renders the main-attributes tab fragment for an item.
-
#tab_publications ⇒ void
Renders the publications tab fragment for an item.
-
#tab_relatives ⇒ void
Renders the relatives tab fragment for an item.
-
#tab_reviews ⇒ void
Renders the reviews tab fragment, loading recent Reviews.io product reviews and stats for the item's SKU.
-
#tab_revisions ⇒ void
Renders the revisions tab fragment for an item.
-
#tab_specifications ⇒ void
Renders the specifications tab fragment for an item.
-
#tab_store_items ⇒ void
Renders the store-items tab fragment for an item.
-
#tab_suppliers ⇒ void
Renders the suppliers tab fragment, loading the item's supplier items and open purchase-order items.
-
#tab_videos ⇒ void
Renders the videos tab fragment for an item.
-
#update ⇒ Object
PUT /items/1 PUT /items/1.xml.
-
#update_amazon ⇒ void
Updates the Amazon-specific attributes of an item, replacing its image profiles wholesale when new profile attributes are submitted.
-
#update_boxes_from_discrepancy_packing ⇒ void
Updates the item's boxes from its discrepancy packing record and clears the packaging review flags on success.
Methods included from Controllers::ContentLocalizable
#allow_content_locale_switch, #default_url_options, #set_content_locale
Methods included from Controllers::RequireTurboFrame
require_in_pane_tab_frames, require_turbo_frame
Methods included from Controllers::Destroyable
Methods inherited from CrmController
#access_denied, #context_id, #context_object, #crm_home_path, #current_ability, #default_url_options, #download_temp, #get_tempfile_path_for_download, #init_status_job_collector, #initialize_crm_lazy_chunks, #persist_enqueued_status_jobs, #record_not_found, #redirect_to_job_or_fallback, #set_context, #set_download_path, #stash_file_for_temp_download, #sync_admin_presence_cookie, #touch_employee_last_seen
Methods inherited from ApplicationController
#account_impersonated?, #add_to_flash, #after_sign_in_path_for, #bypass_forgery_protection?, #chat_enabled?, #cloudflare_cleared?, #default_catalog, #default_url_options, #enable_turbo_frames, #find_publication, #fix_invalid_accept_header, #init_js_utils, #is_globals_call?, #layout_by_resource, #locale_store, #redirect_to, #require_employee_for_crm, #set_base_host, #set_real_ip, #set_report_errors_for, #should_render_layout?, #skip_layout_for_turbo_frame?, #stamp_impersonation_context, #tab_frame_breakout_request?, #warmlyyours_canada_ip?, #warmlyyours_ip?, #y
Methods included from Controllers::ReturnPathHandling
#check_for_return_path, #redirect_to_return_path_or_default
Methods included from Controllers::AnalyticsEvents
#consume_queued_analytics_events, #registration_lead_type, #track_event
Methods included from Controllers::DeviceDetection
Methods included from Controllers::SubdomainDetection
#is_crm_request?, #is_www_request?, #json_request?
Methods included from Controllers::TurboSafeRedirect
Methods included from Controllers::TrackingDetection
#bot_request?, #gdpr_country?, #gdpr_country_data, #prevent_bots, #set_tracking_cookie, #track_visitor?
Methods included from Controllers::AcceleratedFileSending
#send_file_accelerated, #send_upload_accelerated
Methods included from Controllers::ErrorRendering
#excp_string, #mail_to_for_error_reporting, #render_400, #render_404, #render_406, #render_410, #render_500, #render_invalid_authenticity_token, #render_ip_spoof_error, #render_unpermitted_parameters, #safe_referer_or_fallback
Methods included from Controllers::TurnstileVerification
#load_turnstile_script_tag, #turnstile_lazy_widget, #turnstile_script_tag, #turnstile_widget, #validate_turnstile!
Methods included from Controllers::CloudflareCaching
edge_cached, #edge_cached_action?, #reset_cloudflare_cache, #set_cloudflare_cache, #skip_edge_cache!, #skip_session
Methods included from Controllers::Webpackable
#preload_webpack_fonts, #webpack_css_include, #webpack_css_url, #webpack_js_include, #wpd_is_running?
Methods included from Controllers::Localizable
#cloudflare_country_locale, #determine_request_locale, #geocoder_locale, #guest_user_locale_check, #locale_optional_www_auth_path?, #param_locale, #set_locale, #set_request_locale, #skip_localization?, #warmlyyours_ip_locale
Methods included from Controllers::Authenticable
#access_denied, #authenticate_account, #authenticate_account!, #authenticate_account_from_login_token!, #check_is_a_manager, #check_is_a_sales_manager, #check_is_an_admin, #check_is_an_employee, #check_party, #clear_mismatched_guest_user, #create_guest_user, #credentials?, #current_or_guest_user, #current_or_guest_user_id_read_only, #current_user, #devise_mapping, #fully_logged_in?, #generate_bot_id, #guest_user, #identifiable?, #init_current_user, #initialize_guest, #load_context_user, #logging_in, #resource, #resource_name, #restrict_access_for_non_employees, #scrubbed_request_path, #user_object, #warn_on_session_guest_id_leak
Methods included from UrlsHelper
#catalog_breadcrumb_links, #catalog_link, #catalog_link_for_product_line, #catalog_link_for_sku, #cms_link, #delocalized_path, #path_to_sales_product_sku, #path_to_sales_product_sku_for_product_line, #path_to_sales_product_sku_for_product_line_slug, #product_line_from_catalog_link, #protocol_neutral_url, #sanitize_external_url, #valid_external_url?
Instance Method Details
#apply_template_specs ⇒ void
This method returns an undefined value.
Creates the item's template specifications and refreshes the rendered
specifications.
216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'app/controllers/items_controller.rb', line 216 def apply_template_specs @item = Item.find(params[:id]) (:update, @item) res = @item.create_template_specifications if res.errors.blank? @item.update_rendered_product_specifications(user_id: current_user.id) flash[:info] = 'Template specs applied' else flash[:error] = "Template Spec could not be created: #{res.errors.to_sentence.capitalize}" end redirect_to item_path(@item, tab: 'specifications') end |
#auto_translate_attributes ⇒ void
This method returns an undefined value.
Queues (or runs inline in development) attribute translation for the item.
164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'app/controllers/items_controller.rb', line 164 def auto_translate_attributes @item = Item.find(params[:id]) (:update, @item) = { item_id: @item.id, reset_previous_values: params[:reset_previous_values].to_b, attributes: true }.stringify_keys if Rails.env.development? ItemTranslationWorker.new.perform() flash[:info] = 'Translation completed and specs refreshed' else ItemTranslationWorker.perform_async() flash[:info] = 'Attribute Translation Queued' end redirect_to_return_path_or_default item_path(@item) end |
#auto_translate_product_specifications ⇒ void
This method returns an undefined value.
Queues (or runs inline in development) product-specification translation
for the item.
181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'app/controllers/items_controller.rb', line 181 def auto_translate_product_specifications @item = Item.find(params[:id]) (:update, @item) = { item_id: @item.id, reset_previous_values: params[:reset_previous_values].to_b, specs: true }.stringify_keys if Rails.env.development? ItemTranslationWorker.new.perform() flash[:info] = 'Translation completed and specs refreshed' else ItemTranslationWorker.perform_async() flash[:info] = 'Specs Translation Queued' end redirect_to_return_path_or_default item_path(@item) end |
#clone ⇒ void
This method returns an undefined value.
Renders the clone/revise form for an item, offering refurbish when no
refurbished version exists yet.
256 257 258 259 260 261 262 263 264 265 |
# File 'app/controllers/items_controller.rb', line 256 def clone @item = Item.find(params[:id]) @item_actions = [['Clone/Revise', :clone]] if @item.refurbished_version flash[:warning] = 'Item already has a refurbished version, refurbish is disabled' else @allow_catalog_selection = true @item_actions << ['Create refurbished version (-BTK)', :refurbish] end end |
#consolidate_kit ⇒ void
This method returns an undefined value.
Consolidates all kit fields from the kit's component items.
318 319 320 321 322 323 324 325 326 |
# File 'app/controllers/items_controller.rb', line 318 def consolidate_kit @item = Item.kits.find(params[:id]) (:update, @item) Item.transaction do Item::KitConsolidator.new(@item).consolidate_all_fields.commit end flash[:info] = 'Item Kit has been consolidated.' redirect_to @item end |
#copy_related_items_from_siblings ⇒ void
This method returns an undefined value.
Copies related items from the item's siblings, optionally restricted to
params[:relation_type].
538 539 540 541 542 543 544 |
# File 'app/controllers/items_controller.rb', line 538 def @item = Item.find(params[:id]) (:update, Item) result = Item::CopyRelatedItemsFromSiblings.new.process(item: @item, relation_type: params[:relation_type].presence) flash[:info] = result. redirect_to item_path(tab: 'relatives') end |
#create ⇒ Object
POST /items
POST /items.xml
475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'app/controllers/items_controller.rb', line 475 def create @item = Item.new(params[:item]) (:create, @item) # @item.generate_store_items if @item.save @item.create_template_specifications @item.update_rendered_product_specifications(user_id: current_user.id) redirect_to(@item) else render :new, status: :unprocessable_content end end |
#do_clone ⇒ void
This method returns an undefined value.
Performs the clone/revise/refurbish operation via Item::Cloner and
redirects to the new item on success.
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'app/controllers/items_controller.rb', line 270 def do_clone original_item = Item.find(params[:id]) action_type = params[:item][:action_type] catalog_ids = params.dig(:item, :catalogs)&.reject(&:empty?)&.map(&:to_i) || [] (:create, original_item) res = Item::Cloner.new.process(original_item, action_type, catalog_ids) if res.success.to_b flash[:info] = res. redirect_to_return_path_or_default item_path(res.item) else flash[:error] = res. redirect_to_return_path_or_default item_path(original_item) end end |
#edit ⇒ Object
GET /items/1/edit
457 458 459 460 461 |
# File 'app/controllers/items_controller.rb', line 457 def edit @item = Item.find(params[:id]) (:update, @item) render_edit_action(status: :ok) end |
#edit_amazon ⇒ void
This method returns an undefined value.
Renders the Amazon-specific editor for an item.
337 338 339 340 341 |
# File 'app/controllers/items_controller.rb', line 337 def edit_amazon @item = Item.find(params[:id]) (:update, @item) set_image_ransack end |
#edit_catalogs ⇒ void
This method returns an undefined value.
Renders the catalog-assignment editor for an item.
330 331 332 333 |
# File 'app/controllers/items_controller.rb', line 330 def edit_catalogs @item = Item.find(params[:id]) (:update, @item) end |
#edit_kit_contents ⇒ Object
Dedicated editor for kit contents only
464 465 466 467 468 469 470 471 |
# File 'app/controllers/items_controller.rb', line 464 def edit_kit_contents @item = Item.find(params[:id]) (:update, @item) # Render dedicated kit editor view/partials params[:section] = 'kit_contents' @kit_only = true # Implicitly renders app/views/items/edit_kit_contents.html.erb end |
#export_amalytics_snapshot ⇒ void
This method returns an undefined value.
Exports an Amalytix XLSX snapshot of the item's Amazon seller catalog
items and streams the file.
832 833 834 835 836 837 838 839 840 |
# File 'app/controllers/items_controller.rb', line 832 def export_amalytics_snapshot catalog_items = CatalogItem.amazons_sellers.joins(store_items: :item).merge(StoreItem.available.warmlyyours_warehouses).where(items: { id: params[:id] }) file_name = "amalytix_export_#{Time.current.to_i}.xlsx" directory_path = Rails.root.join(Rails.application.config.x.temp_storage_path.to_s, 'users', current_user.id.to_s) FileUtils.mkdir_p(directory_path) output_file_path = Rails.root.join(directory_path, file_name) Amazon::ExportAmalyticsSnapshot.new.process(catalog_items:, output_file_path:) send_file_accelerated(output_file_path) end |
#feature_update ⇒ void
This method returns an undefined value.
Updates an item inline from the feature grid and responds with best-in-place JSON.
820 821 822 823 824 825 826 827 |
# File 'app/controllers/items_controller.rb', line 820 def feature_update @item = Item.find(params[:id]) (:update, @item) @item.update(params[:item]) respond_to do |format| format.json { respond_with_bip(@item) } end end |
#features ⇒ void
This method returns an undefined value.
Renders the feature grid of items, defaulting to items public and active
in the main catalogs.
810 811 812 813 814 815 816 |
# File 'app/controllers/items_controller.rb', line 810 def features params[:q] ||= {} params[:q][:public_and_active_in_catalog_id] ||= [1, 2] @q = Item.non_publications.ransack(params[:q]) @q.sorts = 'sku' if @q.sorts.blank? @pagy, @items = pagy(@q.result, limit: 50) end |
#flush_cache ⇒ void
This method returns an undefined value.
Touches the item and queues an edge-cache purge for it.
844 845 846 847 848 849 850 |
# File 'app/controllers/items_controller.rb', line 844 def flush_cache @item = Item.find(params[:id]) (:update, @item) @item.touch @item.purge_edge_cache redirect_to item_path(@item), notice: 'Cache purge has been queued, this can take a couple minutes.' end |
#identifier_label ⇒ void
This method returns an undefined value.
Streams a UPC/item-identifier PDF label, honoring upc params for size,
Amazon Transparency output, serial number, and target market.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'app/controllers/items_controller.rb', line 64 def identifier_label @item = Item.find(params[:id]) upc_params = params[:upc] || {} size = upc_params[:size].presence || 'large' output_amazon_transparency = upc_params[:output_amazon_transparency].presence || false serial_number = upc_params[:serial_number].presence target_market = upc_params[:target_market].presence&.to_sym || :us_ca result = Pdf::Label::ItemIdentifier.call(@item, size: size, skip_claim_amazon_transparency: true, output_amazon_transparency: output_amazon_transparency, target_market: target_market, serial_number: serial_number) send_data result.pdf, filename: result.file_name, type: 'application/pdf', disposition: 'inline', encoding: 'utf8' end |
#import_amazon_images ⇒ Object
POST /items/:id/import_amazon_images
Imports missing Amazon images from live catalog data into our image library
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'app/controllers/items_controller.rb', line 375 def import_amazon_images @item = Item.find(params[:id]) (:update, @item) # Use specific catalog item if provided, otherwise service will use first active Amazon catalog catalog_item = params[:catalog_item_id].present? ? CatalogItem.find_by(id: params[:catalog_item_id]) : nil result = Catalog::AmazonImageBackfillService.new(@item, catalog_item: catalog_item).process if result[:errors].any? flash[:error] = "Import completed with errors: #{result[:errors].join(', ')}" elsif result[:imported].positive? flash[:notice] = "Successfully imported #{result[:imported]} image(s). #{result[:skipped]} slot(s) skipped." else flash[:notice] = "No new images to import. #{result[:skipped]} slot(s) already have images or Amazon has no image." end redirect_to_return_path_or_default item_path(@item, tab: 'amazon') end |
#index ⇒ void
This method returns an undefined value.
Renders the item search/list page.
18 |
# File 'app/controllers/items_controller.rb', line 18 def index; end |
#item_label ⇒ void
This method returns an undefined value.
Streams a storage-location PDF label for the given item ids.
86 87 88 89 90 91 92 93 94 |
# File 'app/controllers/items_controller.rb', line 86 def item_label @items = Item.where(id: params[:id]) result = Pdf::Label::StorageLocationItem.call(items: @items) send_data result.pdf, filename: result.file_name, type: 'application/pdf', disposition: 'inline', encoding: 'utf8' end |
#lookup ⇒ void
This method returns an undefined value.
Returns JSON autocomplete results for item lookup, honoring scope,
kit, exclusion, and condition filters.
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 |
# File 'app/controllers/items_controller.rb', line 506 def lookup if (item_id = params[:item_id]) @results = Item.where(id: item_id) else @results = case params[:scope] when 'all' Item.all when 'publications' Item.publications else Item.non_publications end @results = @results.active unless params[:include_inactive].to_b @results = @results.where.not(id: params[:exclude_item_ids]) if params[:exclude_item_ids].present? @results = @results.where(condition: params[:item_conditions]) if params[:item_conditions].present? if params[:kits] == 'only' @results = @results.kits elsif params[:kits] == 'exclude' @results = @results.non_kits end @results = if (q = params[:q].presence || params[:term].presence) @results.wild_search(q) else @results.order(Item[:sku]) end end render json: TomSelect.format_json_results(self, @results, params[:page], params[:per_page]) end |
#make_primary_image ⇒ void
This method returns an undefined value.
Makes the image given by params[:image_id] the item's primary image.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'app/controllers/items_controller.rb', line 98 def make_primary_image @item = Item.find(params[:id]) (:update, @item) if (img = Image.find(params[:image_id])) @item.primary_image = img if @item.save flash[:info] = "Image #{params[:image_id]} is not primary image of item #{@item.sku}" else flash[:error] = "Could not make image id #{params[:image_id]} the primary image of item #{@item.sku}" end else flash[:error] = "Image #{params[:image_id]} not found" end redirect_to_return_path_or_default item_path(@item) end |
#new ⇒ Object
GET /items/new
GET /items/new.xml
451 452 453 454 |
# File 'app/controllers/items_controller.rb', line 451 def new (:create, Item) @item = Item.new(params[:item]) end |
#print_identifier_label ⇒ void
This method returns an undefined value.
Prints one or more identifier labels via a PrintProfile, or stashes the
combined PDF for download when no print profile is selected.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'app/controllers/items_controller.rb', line 117 def print_identifier_label require 'hexapdf' @item = Item.find(params[:id]) (:read, @item) print_profile = PrintProfile.where(id: params.dig(:upc, :print_profile_id)).first quantity = params.dig(:upc, :quantity)&.to_i || 1 size = params.dig(:upc, :size).presence || 'large' target_market = params.dig(:upc, :target_market) serial_number = params.dig(:upc, :serial_number).presence output_amazon_transparency = params.dig(:upc, :output_amazon_transparency).to_b amazon_transparency_code = params.dig(:upc, :amazon_transparency_code).presence if quantity.between?(1, 100) pdf = PdfCombinator.new quantity.times do upc_result = Pdf::Label::ItemIdentifier.call(@item, size: size, output_amazon_transparency: output_amazon_transparency, amazon_transparency_code: amazon_transparency_code, target_market: target_market, serial_number: serial_number) pdf << upc_result.pdf end if print_profile base64_output = Base64.encode64(pdf.to_pdf) r = print_profile.print(base64_output, quantity: 1) flash[:info] = "UPC label for #{@item.sku} sent to printer: #{print_profile.printer.name} with job id #{r}" else # Stash the PDF for the download-bridge Stimulus controller to surface # via the Jobs offcanvas + auto-trigger. Streaming binary back from a # Turbo-submitted form gets corrupted by TurboStreamFlashable. res = get_tempfile_path_for_download("#{@item.sku}_identifier_label.pdf") File.binwrite(res[:local_file_path], pdf.to_pdf) session[:download_path] = res[:request_path] session[:download_disposition] = :inline flash[:info] = "PDF generated for #{@item.sku} (#{quantity} label#{'s' if quantity > 1})" end redirect_to_return_path_or_default(item_path(@item, tab: 'logistics')) else flash[:error] = "Print Profile #{params[:print_profile_id]} missing or unknown or quantity exceeds limit of 100" redirect_to_return_path_or_default(item_path(@item, tab: 'logistics')) end end |
#print_label ⇒ void
This method returns an undefined value.
Renders the printable label view for an item.
57 58 59 |
# File 'app/controllers/items_controller.rb', line 57 def print_label @item = Item.find(params[:id]) end |
#prune_empty_specs ⇒ void
This method returns an undefined value.
Removes empty template specifications from the item and refreshes the
rendered specifications.
232 233 234 235 236 237 238 239 |
# File 'app/controllers/items_controller.rb', line 232 def prune_empty_specs @item = Item.find(params[:id]) (:update, @item) @item.prune_empty_specs @item.update_rendered_product_specifications(user_id: current_user.id) flash[:info] = 'Empty template specs removed if any' redirect_to item_path(@item, tab: 'specifications') end |
#publication ⇒ void
This method returns an undefined value.
Redirects to the authenticated URL of a publication item.
243 244 245 246 247 248 249 250 251 |
# File 'app/controllers/items_controller.rb', line 243 def publication @item = Item.publications.find(params[:id]) (:read, @item) if @item redirect_to @item.authenticated_url else render_404 end end |
#push_amazon_listing_data_all ⇒ Object
POST /items/:id/push_amazon_listing_data_all
Pushes listing data to all active Amazon catalog items for this item
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
# File 'app/controllers/items_controller.rb', line 396 def push_amazon_listing_data_all @item = Item.find(params[:id]) (:update, @item) catalog_item_ids = @item.catalog_items.amazons.active.ids if catalog_item_ids.empty? flash[:warning] = 'No active Amazon catalog items found for this item.' redirect_to_return_path_or_default item_path(@item, tab: 'amazon') return end amazon_opts = { catalog_item_ids: catalog_item_ids, operation: 'PATCH', redirect_to: item_path(@item, tab: 'amazon') } job_id = AmazonItemOperationWorker.perform_async(**amazon_opts) job_id ||= SidekiqUniqueJobsJidLookup.active_jid_for_args(AmazonItemOperationWorker, amazon_opts) flash[:info] = "Queued listing data push for #{catalog_item_ids.size} Amazon catalog item(s). Job ID: #{job_id}" redirect_to_return_path_or_default item_path(@item, tab: 'amazon') end |
#refresh_embedding ⇒ void
This method returns an undefined value.
Regenerates the item's search embedding, using chunked embeddings for
publications.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
# File 'app/controllers/items_controller.rb', line 289 def @item = Item.find(params[:id]) (:update, @item) # Use chunked embeddings for publications (PDFs can be large) begin if @item.is_publication? = @item.(:primary, force: true) if .one? flash[:info] = 'Embedding refreshed successfully' elsif .any? flash[:info] = "Generated #{.size} chunk embeddings successfully" else flash[:warning] = 'Failed to generate embeddings' end elsif @item.(:primary, force: true) flash[:info] = 'Embedding refreshed successfully' else flash[:warning] = 'Failed to generate embedding' end rescue Embedding::Gemini::Error => e flash[:warning] = "Failed to generate embedding: #{e.}" end redirect_to_return_path_or_default item_path(@item, tab: 'publications') end |
#refresh_rendered_product_specifications ⇒ void
This method returns an undefined value.
Rebuilds the item's rendered product specifications, optionally limited to
the given params[:tokens].
198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'app/controllers/items_controller.rb', line 198 def refresh_rendered_product_specifications @item = Item.find(params[:id]) (:update, @item) tokens = params[:tokens].presence res = @item.update_rendered_product_specifications(user_id: current_user.id, tokens: tokens) if res == :success flash[:info] = 'Specs refreshed' elsif res == :skipped flash[:warning] = 'Specs refresh were skipped (perhaps publication or unsaved item)' else flash[:error] = "Error refreshing specs, #{@item.errors_to_s}." end redirect_to_return_path_or_default item_path(@item, tab: 'specifications') end |
#show ⇒ void
This method returns an undefined value.
Shows a single item, found by id or SKU, redirecting publication items to
the publication view unless force_item_view is set. Supports
?refresh_specs=1 to rebuild rendered product specifications first.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'app/controllers/items_controller.rb', line 24 def show @item = Item.find_by(id: params[:id]) || Item.find_by(sku: params[:id]) if @item.nil? flash[:error] = "Item #{params[:id]} not found" redirect_to items_path elsif @item.id.to_s != params[:id].to_s redirect_to item_path(@item) elsif @item.is_publication? && params[:force_item_view].blank? redirect_to publication_path(@item) else (:read, @item) if params[:refresh_specs].to_b @item.update_rendered_product_specifications(user_id: current_user.id) @item.reload end # When a form (e.g. custom_mats#create, image profile modal) submits # with Turbo Drive and we redirect here, Turbo's follow-up GET reuses # the original `Accept: text/vnd.turbo-stream.html` header. There is # no show.turbo_stream template, so: # - returning `head :no_content` (204) makes Turbo "do nothing" and # the user sees the form spin and clear with no navigation; # - omitting the format.turbo_stream block raises 406 UnknownFormat. # Render the HTML show template with an explicit text/html Content-Type # so Turbo Drive treats it as a normal page response and replaces <body>. respond_to do |format| format.html format.turbo_stream { render :show, formats: [:html], content_type: 'text/html' } end end end |
#tab_alerts ⇒ Object
Tab actions for turbo-tabs lazy loading
547 548 549 550 551 552 553 554 |
# File 'app/controllers/items_controller.rb', line 547 def tab_alerts @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_all_images ⇒ void
This method returns an undefined value.
Renders the all-images tab, collecting every image related to the item
with optional tag and locale filters.
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 |
# File 'app/controllers/items_controller.rb', line 658 def tab_all_images @item = Item.find(params[:id]) (:read, @item) @selected_tag = params[:tag].presence @image_locale = params[:image_locale].presence tag_filter = @selected_tag == 'all' ? nil : @selected_tag locale_filter = @image_locale == 'all' ? nil : @image_locale @all_images = Item::ImageRetriever.new(tags: [tag_filter].compact.uniq.presence, locales: locale_filter, include_secondary_product_lines: true, product_line_query_limit: 500, max_images: 500, respect_primary_image_tags: true).process(@item).all_images ActiveRecord::Associations::Preloader.new(records: @all_images, associations: [{ taggings: :tag }]).call @image_profile_types_by_image_id = ImageProfile .where(image_id: @all_images.map(&:id), item_id: @item.id) .group_by(&:image_id) .transform_values { |recs| recs.to_set(&:image_type) } @image_profile_types_in_item = ImageProfile .where(item_id: @item.id) .then { |q| locale_filter.present? ? q.where(locale: locale_filter) : q } .distinct .pluck(:image_type) .to_set @all_tags = (['all'] + Image.).uniq.compact.sort @all_locales = (['all'] + Image.pluck(Arel.sql('distinct unnest(locales) as locale'))).uniq.compact.sort render layout: false end |
#tab_amazon ⇒ void
This method returns an undefined value.
Renders the Amazon tab fragment for an item.
787 788 789 790 791 792 793 794 |
# File 'app/controllers/items_controller.rb', line 787 def tab_amazon @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_catalog_items ⇒ void
This method returns an undefined value.
Renders the catalog-items tab fragment for an item.
624 625 626 627 628 629 630 631 |
# File 'app/controllers/items_controller.rb', line 624 def tab_catalog_items @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_classifications ⇒ void
This method returns an undefined value.
Renders the classifications tab fragment for an item.
580 581 582 583 584 585 586 587 |
# File 'app/controllers/items_controller.rb', line 580 def tab_classifications @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_descriptions ⇒ void
This method returns an undefined value.
Renders the descriptions tab fragment for an item.
569 570 571 572 573 574 575 576 |
# File 'app/controllers/items_controller.rb', line 569 def tab_descriptions @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_faqs ⇒ void
This method returns an undefined value.
Renders the FAQs tab fragment for an item.
725 726 727 728 729 730 731 732 |
# File 'app/controllers/items_controller.rb', line 725 def tab_faqs @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_floor_plans ⇒ void
This method returns an undefined value.
Renders the floor-plans tab fragment for an item.
700 701 702 703 704 705 706 707 |
# File 'app/controllers/items_controller.rb', line 700 def tab_floor_plans @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_images ⇒ void
This method returns an undefined value.
Renders the images tab fragment for an item.
646 647 648 649 650 651 652 653 |
# File 'app/controllers/items_controller.rb', line 646 def tab_images @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_kit_contents ⇒ void
This method returns an undefined value.
Renders the kit-contents tab fragment, loading the kit's component item
relations.
763 764 765 766 767 768 769 770 771 |
# File 'app/controllers/items_controller.rb', line 763 def tab_kit_contents @item = Item.find(params[:id]) (:read, @item) @item_relations = @item.target_item_relations.kit_components.joins(target_item: :product_category).order(ProductCategory[:priority], Item[:sku]) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_kits ⇒ void
This method returns an undefined value.
Renders the kits tab fragment, listing the kits this item belongs to.
775 776 777 778 779 780 781 782 783 |
# File 'app/controllers/items_controller.rb', line 775 def tab_kits @item = Item.find(params[:id]) (:read, @item) @item_relations = @item.source_item_relations.kit_components.joins(:source_item).order(Item[:is_discontinued], Item[:sku]) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_logistics ⇒ void
This method returns an undefined value.
Renders the logistics tab fragment for an item.
602 603 604 605 606 607 608 609 |
# File 'app/controllers/items_controller.rb', line 602 def tab_logistics @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_main_attributes ⇒ void
This method returns an undefined value.
Renders the main-attributes tab fragment for an item.
558 559 560 561 562 563 564 565 |
# File 'app/controllers/items_controller.rb', line 558 def tab_main_attributes @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_publications ⇒ void
This method returns an undefined value.
Renders the publications tab fragment for an item.
736 737 738 739 740 741 742 743 |
# File 'app/controllers/items_controller.rb', line 736 def tab_publications @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_relatives ⇒ void
This method returns an undefined value.
Renders the relatives tab fragment for an item.
798 799 800 801 802 803 804 805 |
# File 'app/controllers/items_controller.rb', line 798 def tab_relatives @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_reviews ⇒ void
This method returns an undefined value.
Renders the reviews tab fragment, loading recent Reviews.io product
reviews and stats for the item's SKU.
712 713 714 715 716 717 718 719 720 721 |
# File 'app/controllers/items_controller.rb', line 712 def tab_reviews @item = Item.find(params[:id]) (:read, @item) @item_reviews = ReviewsIo.active.visible.product_reviews.by_sku(@item.sku).recent.limit(50) @review_stats = ReviewsIo.stats_for_skus([@item.sku]) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_revisions ⇒ void
This method returns an undefined value.
Renders the revisions tab fragment for an item.
635 636 637 638 639 640 641 642 |
# File 'app/controllers/items_controller.rb', line 635 def tab_revisions @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_specifications ⇒ void
This method returns an undefined value.
Renders the specifications tab fragment for an item.
591 592 593 594 595 596 597 598 |
# File 'app/controllers/items_controller.rb', line 591 def tab_specifications @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_store_items ⇒ void
This method returns an undefined value.
Renders the store-items tab fragment for an item.
613 614 615 616 617 618 619 620 |
# File 'app/controllers/items_controller.rb', line 613 def tab_store_items @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_suppliers ⇒ void
This method returns an undefined value.
Renders the suppliers tab fragment, loading the item's supplier items and
open purchase-order items.
748 749 750 751 752 753 754 755 756 757 758 |
# File 'app/controllers/items_controller.rb', line 748 def tab_suppliers @item = Item.find(params[:id]) (:read, @item) (:read, Supplier) @supplier_items = @item.supplier_items @purchase_order_items = @item.purchase_order_items.joins(:purchase_order).includes(:purchase_order).where(state: %w[not_receipted partially_receipted]) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#tab_videos ⇒ void
This method returns an undefined value.
Renders the videos tab fragment for an item.
689 690 691 692 693 694 695 696 |
# File 'app/controllers/items_controller.rb', line 689 def tab_videos @item = Item.find(params[:id]) (:read, @item) respond_to do |format| format.html { render layout: should_render_layout? } format.turbo_stream end end |
#update ⇒ Object
PUT /items/1
PUT /items/1.xml
490 491 492 493 494 495 496 497 498 499 500 501 |
# File 'app/controllers/items_controller.rb', line 490 def update @item = Item.find(params[:id]) (:update, @item) # Ensure kits save cleanly when editing kit contents update_attrs = params.require(:item).permit! if @item.update(update_attrs) @item.discontinue_store_and_catalog_items if @item.is_discontinued? redirect_to_return_path_or_default(@item) else render_edit_action end end |
#update_amazon ⇒ void
This method returns an undefined value.
Updates the Amazon-specific attributes of an item, replacing its image
profiles wholesale when new profile attributes are submitted.
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'app/controllers/items_controller.rb', line 346 def update_amazon @item = Item.find(params[:id]) (:update, @item) @item.refresh_specs = true item_params = params[:item].to_h image_profiles_attributes = item_params[:image_profiles_attributes] || {} image_profiles_attributes.transform_values! do |v| v.delete(:id) v end save_result = false Item.transaction do if image_profiles_attributes.present? # Delete all of them first, its a lot easier than trying to figure out which ones to delete @item.image_profiles.destroy_all end save_result = @item.update(item_params) end if save_result redirect_to_return_path_or_default item_path(@item, tab: 'amazon') else flash.now[:error] = @item.errors_to_s.presence || 'Could not update Amazon item.' set_image_ransack render :edit_amazon, status: :unprocessable_content end end |
#update_boxes_from_discrepancy_packing ⇒ void
This method returns an undefined value.
Updates the item's boxes from its discrepancy packing record and clears
the packaging review flags on success.
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
# File 'app/controllers/items_controller.rb', line 423 def update_boxes_from_discrepancy_packing @item = Item.find(params[:id]) (:update, @item) all_good = false err_msgs = [] if @item.packaging_discrepancy_packing package = @item.packaging_discrepancy_packing.to_packages.select { |p| p.contents&.first&.item_id == @item.id } if package if @item.update_boxes_from_packages(package) all_good = true flash[:info] = 'Item boxes have been updated.' @item.update_column(:review_product_packaging_flag, false) @item.update_column(:packaging_discrepancy_packing_id, nil) else err_msgs = @item.errors. end else err_msgs << "Could not match package to item, packages: #{@item.packaging_discrepancy_packing.to_packages}" end else err_msgs << "Could not find packing ID: #{@item.packaging_discrepancy_packing_id}" end flash[:error] = "Item boxes could not be updated. #{err_msgs.join(', ')}" unless all_good redirect_to edit_item_path(@item, section: 'logistics') end |