Class: Crm::ExportedCatalogItemPacketsController

Inherits:
CrmController show all
Includes:
Controllers::Attachable, Controllers::RequireTurboFrame
Defined in:
app/controllers/crm/exported_catalog_item_packets_controller.rb

Overview

Controller: exported catalog item packets.

Constant Summary collapse

TAB_ACTIONS =

Tab fragments never render standalone — non-frame hits redirect to show ?tab=.

%i[tab_images tab_literature tab_attributes tab_items tab_uploads tab_history].freeze

Constants included from Controllers::Attachable

Controllers::Attachable::PUBLICATIONS_PER_PAGE

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

Methods included from Controllers::RequireTurboFrame

require_in_pane_tab_frames, require_turbo_frame

Methods included from Controllers::Attachable

#attach, #publication_modal, #remove_attachment, #search_library

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, #render_edit_action, #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

#device_detector, #is_ie?

Methods included from Controllers::SubdomainDetection

#is_crm_request?, #is_www_request?, #json_request?

Methods included from Controllers::TurboSafeRedirect

#redirect_to

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

#add_image_attributeObject

Adds a new image-URL/name attribute pair to the packet, indexed after the
highest existing image_url_N attribute.



314
315
316
317
318
319
320
321
322
323
324
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 314

def add_image_attribute
  @packet = ExportedCatalogItemPacket.find(params[:id])
  existing_indices = @packet.exported_catalog_item_packet_attributes
                            .where("attr_name LIKE 'image_url_%'")
                            .pluck(:attr_name)
                            .map { |n| n[/image_url_(\d+)/, 1].to_i }
  next_idx = (existing_indices.max || 0) + 1
  @packet.exported_catalog_item_packet_attributes.create!(attr_type: 'image', attr_name: "image_url_#{next_idx}")
  @packet.exported_catalog_item_packet_attributes.create!(attr_type: 'image', attr_name: "image_name_#{next_idx}")
  redirect_to "#{edit_exported_catalog_item_packet_path(@packet)}#attributes"
end

#add_items_to_packetObject

Adds a saved search's result items to the selected packet.



154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 154

def add_items_to_packet
  packet_params = params[:packet] || {}
  @search = Search.find(packet_params[:search_id])
  if packet_params[:exported_catalog_item_packet_id].empty?
    flash[:error] = 'You have to select a packet before to continue.'
    redirect_back_or_to root_path
  else
    @packet = ExportedCatalogItemPacket.find(packet_params[:exported_catalog_item_packet_id])
    values = @search.search_results.pluck(:resource_id).map { |rid| { catalog_item_id: rid, exported_catalog_item_packet_id: @packet.id } }
    ExportedCatalogItem.insert_all(values) if values.any?
    redirect_to edit_exported_catalog_item_packet_path(@packet)
  end
end

#assignObject

Renders the form to assign a saved search's results to a packet.



149
150
151
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 149

def assign
  @search = Search.find(params[:search_id])
end

#createObject

Placeholder — packets are created via #mass_create.



42
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 42

def create; end

#delete_item_from_packetObject

Removes an item from the packet, pruning attributes no longer backed by
any remaining item's rendered product specifications.



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 284

def delete_item_from_packet
  @packet = ExportedCatalogItemPacket.find(params[:id])
  item = @packet.exported_catalog_items.find_by(id: params[:item])
  if item.present?
    item.destroy
    keys = @packet.exported_catalog_items.map { |ci| ci.catalog_item.item.rendered_product_specifications.keys }.flatten.uniq
    @packet.exported_catalog_item_packet_attributes.map(&:attr_name).flatten.uniq
    @packet.exported_catalog_item_packet_attributes.each do |a|
      a.destroy unless keys.include?(a.attr_name)
    end
  else
    flash[:error] = 'Item not found in the packet.'
  end

  redirect_to edit_exported_catalog_item_packet_path(@packet)
end

#do_generateObject

Generates the packet per the submitted export options — synchronously in
development, via a background worker otherwise.



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 354

def do_generate
  @packet = ExportedCatalogItemPacket.find(params[:id])
  options = params[:options] || {}
  export_csv = options[:export_csv].to_b
  export_xlsx = options[:export_xlsx].to_b
  export_images = options[:export_images].to_b
  export_literatures = options[:export_literatures].to_b
  if Rails.env.development?
    @packet.generate_packet(export_csv:, export_xlsx:,
                                     export_images:,
                                     export_literatures:)
    redirect_to exported_catalog_item_packet_path(@packet.id, anchor: "uploads")
  else
    options = {
      packet_id: @packet.id,
      return_path: exported_catalog_item_packet_path(@packet.id, anchor: "uploads"),
      export_csv:,
      export_xlsx:,
      export_images:,
      export_literatures:
    }.stringify_keys
    job_id = GenerateExportedCatalogPacketWorker.perform_async(options)
    redirect_to_job_or_fallback(job_id, exported_catalog_item_packet_path(@packet.id, anchor: 'uploads'))
  end
end

#download_csvObject

Streams the packet's attributes as a CSV download.



169
170
171
172
173
174
175
176
177
178
179
180
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 169

def download_csv
  @packet = ExportedCatalogItemPacket.find(params[:id])
  tempfile = Tempfile.new(["packet_#{@packet.id}", '.csv'])
  tempfile.flush
  tempfile.fsync
  begin
    @packet.export_csv(tempfile.path)
    send_file_accelerated(tempfile.path, download: true)
  ensure
    tempfile.close # here we cannot use `close!` because of https://github.com/markevans/dragonfly/issues/319
  end
end

#download_xlsxObject

Streams the packet's attributes as an XLSX download.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 183

def download_xlsx
  @packet = ExportedCatalogItemPacket.find(params[:id])
  tempfile = Tempfile.new(["packet_#{@packet.id}", ".xlsx"])
  tempfile.flush
  tempfile.fsync
  begin
    @packet.export_xlsx(tempfile.path)
    send_file tempfile.path,
              filename: "packet_#{@packet.id}_attributes.xlsx",
              type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
              disposition: "attachment"
  ensure
    tempfile.close
  end
end

#editObject

Renders the packet edit form.



36
37
38
39
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 36

def edit
  @packet = ExportedCatalogItemPacket.find(params[:id])
  initialize_for_edit
end

#generateObject

Renders the packet generation options form.



342
343
344
345
346
347
348
349
350
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 342

def generate
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @options = OpenStruct.new({
                              export_csv: false,
                              export_xlsx: true,
                              export_images: true,
                              export_literatures: true
                            })
end

#indexObject

Lists exported catalog item packets, ransack-filtered (defaulting to active).



12
13
14
15
16
17
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 12

def index
  params[:q] ||= { inactive_eq: 0 }
  @q = ExportedCatalogItemPacket.ransack(params[:q])
  @q.sorts = 'updated_at DESC' if @q.sorts.blank?
  @pagy, @packets = pagy(@q.result.includes(:customer, :exported_catalog_items), limit: params[:per])
end

#mass_createObject

Creates a new, empty packet for the customer with an auto-generated name.



141
142
143
144
145
146
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 141

def mass_create
  @customer = Customer.find(params[:customer_id])
  @packet = @customer.exported_catalog_item_packets.new(name: "#{@customer.name.downcase}_#{Time.current.strftime('%m_%d_%Y')}_#{Time.current.to_i}")
  @packet.save
  redirect_to exported_catalog_item_packet_path(@packet)
end

#newObject

Placeholder — new packets are created via #mass_create.



33
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 33

def new; end

#new_packet_attributeObject

Adds a new static attribute row to the packet.



302
303
304
305
306
307
308
309
310
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 302

def new_packet_attribute
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @att = @packet.exported_catalog_item_packet_attributes.new(attr_type: 'static', attr_name: "static_attr_#{Random.rand(1000)}")
  @att.save!
  # initialize_static_attributes
  respond_to do |format|
    format.turbo_stream
  end
end

#refresh_attributesObject

Refreshes rendered product specifications for every item in the packet, synchronously.



200
201
202
203
204
205
206
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 200

def refresh_attributes
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @packet.exported_catalog_items.each do |ci|
    ci.catalog_item.item.update_rendered_product_specifications
  end
  redirect_to "#{edit_exported_catalog_item_packet_path(@packet)}#attributes"
end

#refresh_imagesObject

Relinks the packet's item images.



209
210
211
212
213
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 209

def refresh_images
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @packet.link_images
  redirect_to exported_catalog_item_packet_path(@packet, anchor: "images")
end

#refresh_rendered_product_specificationsObject

Refreshes rendered product specifications for every item in the packet
(synchronously in development, async in other environments).



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 126

def refresh_rendered_product_specifications
  @packet = ExportedCatalogItemPacket.find(params[:id])
  item_ids = @packet.exported_catalog_items.joins(catalog_item: :store_item).pluck(StoreItem[:item_id]).uniq
  items = Item.where(id: item_ids)
  items.find_each do |item|
    if Rails.env.development?
      item.update_rendered_product_specifications
    else
      item.async_update_rendered_product_specifications
    end
  end
  redirect_to_return_path_or_default url_for(action: :show, id: @packet.id)
end

#showObject

Shows a packet's summary (items, attributes, empty-item alert).



20
21
22
23
24
25
26
27
28
29
30
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 20

def show
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @exported_catalog_items = @packet.exported_catalog_items.joins(catalog_item: :item).includes(catalog_item: :item)
  @exported_catalog_items.each do |eci|
    @alert_eci_empty = true if eci.catalog_item.nil?
  end
  @pt_keys = []
  @packet_attributes = @packet.exported_catalog_item_packet_attributes
  @attributes = @packet.exported_catalog_item_packet_attributes.order(:attr_position)
  @tags = ExportedCatalogItemPacketImageAttribute::CORE_TAGS
end

#sort_and_update_attributesObject

In-place edit for a single packet attribute (rename, checkbox, value,
sort order, or connection), routed by params[:source].



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 217

def sort_and_update_attributes
  case params[:source]
  when 'rename'
    ExportedCatalogItemPacketAttribute.find(params[:attr_id]).update(attr_rename: params[:attr_value])
  when 'checkbox'
    ExportedCatalogItemPacketAttribute.find(params[:attr_id]).update(selected: params[:attr_check_value])
  when 'value'
    ExportedCatalogItemPacketAttribute.find(params[:attr_id]).update(attr_value: params[:attr_value])
  when 'sort'
    params[:exported_catalog_item_packet_attribute].each_with_index do |id, index|
      ExportedCatalogItemPacketAttribute.find(id).update(attr_position: index + 1)
    end
  when 'connection'
    ExportedCatalogItemPacketAttribute.find(params[:attr_id]).update(attr_connected_to: params[:attr_value])
  end
  head :ok
end

#tab_attributesObject

In-pane tab: selected packet attributes.



86
87
88
89
90
91
92
93
94
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 86

def tab_attributes
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @attributes = @packet.exported_catalog_item_packet_attributes.where(selected: true).order(:attr_position).to_a
  @exported_catalog_items = @packet.exported_catalog_items.includes(catalog_item: :item)
  respond_to do |format|
    format.html { render layout: should_render_layout? }
    format.turbo_stream
  end
end

#tab_historyObject

In-pane tab: version history.



116
117
118
119
120
121
122
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 116

def tab_history
  @packet = ExportedCatalogItemPacket.find(params[:id])
  respond_to do |format|
    format.html { render layout: should_render_layout? }
    format.turbo_stream
  end
end

#tab_imagesObject

In-pane tab: item images.



70
71
72
73
74
75
76
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 70

def tab_images
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @exported_catalog_items = @packet.exported_catalog_items
                                   .includes(catalog_item: :item, exported_catalog_item_images: :image)
  @tags = ExportedCatalogItemPacketImageAttribute::CORE_TAGS
  render 'show_item_images', layout: should_render_layout?
end

#tab_itemsObject

In-pane tab: packet items.



97
98
99
100
101
102
103
104
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 97

def tab_items
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @exported_catalog_items = @packet.exported_catalog_items
  respond_to do |format|
    format.html { render layout: should_render_layout? }
    format.turbo_stream
  end
end

#tab_literatureObject

In-pane tab: literature.



79
80
81
82
83
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 79

def tab_literature
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @exported_catalog_items = @packet.exported_catalog_items
  render 'show_literature', layout: should_render_layout?
end

#tab_uploadsObject

In-pane tab: generated uploads.



107
108
109
110
111
112
113
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 107

def tab_uploads
  @packet = ExportedCatalogItemPacket.find(params[:id])
  respond_to do |format|
    format.html { render layout: should_render_layout? }
    format.turbo_stream
  end
end

#toggle_image_exclusionObject

Toggles whether a packet image is excluded from export.



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 236

def toggle_image_exclusion
  @packet = ExportedCatalogItemPacket.find(params[:id])
  @image = ExportedCatalogItemImage.joins(:exported_catalog_item)
                                   .merge(@packet.exported_catalog_items)
                                   .find(params[:image_id])

  begin
    @image.update!(excluded: !@image.excluded?)
  rescue ActiveRecord::RecordInvalid => e
    logger.error "toggle_image_exclusion failed for image #{@image.id}: #{e.message}"
    respond_to do |format|
      format.turbo_stream do
        render turbo_stream: turbo_stream.replace(
          "exported-image-#{@image.id}",
          partial: "exported_image_row",
          locals: { ecii: @image.reload, ci: @image.exported_catalog_item, packet: @packet }
        )
      end
      format.html { redirect_to exported_catalog_item_packet_path(@packet, anchor: "packet-images"), alert: e.message }
    end
    return
  end

  respond_to do |format|
    format.turbo_stream do
      render turbo_stream: turbo_stream.replace(
        "exported-image-#{@image.id}",
        partial: "exported_image_row",
        locals: { ecii: @image, ci: @image.exported_catalog_item, packet: @packet }
      )
    end
    format.html { redirect_to exported_catalog_item_packet_path(@packet, anchor: "packet-images") }
  end
end

#transmitObject

Generates the packet and redirects to compose a communication with it attached.



327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 327

def transmit
  @packet = ExportedCatalogItemPacket.find(params[:id])
  params[:export_csv].nil? || params[:export_csv].to_b
  params[:export_images].nil? || params[:export_images].to_b
  params[:export_literatures].nil? || params[:export_literatures].to_b
  upload = @packet.generate_packet
  if upload.persisted?
    redirect_to new_communication_path(resource_id: params[:id], resource_type: 'exported_catalog_item_packet')
  else
    flash[:error] = upload.errors_to_s
    redirect_to exported_catalog_item_packet_path(@packet)
  end
end

#updateObject

Updates a packet, relinking images and refreshing product specification
attribute assignments as requested.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 46

def update
  @packet = ExportedCatalogItemPacket.find(params[:id])

  if @packet.update(params[:exported_catalog_item_packet])
    @packet.link_images if params.dig(:options, :refresh_images).to_b
    @packet.exported_catalog_items.each do |ecil|
      ecil.product_specifications.delete_all
    end
    if params[:attributes].present?
      attributes = params[:attributes]
      attributes.each do |att|
        @packet.exported_catalog_items.each do |eci|
          eci.product_specifications << ProductSpecification.where(id: eci.catalog_item.item.product_specifications.map(&:id), token: att)
        end
      end
    end
    redirect_to exported_catalog_item_packet_path(@packet)
  else
    initialize_for_edit
    render :edit, status: :unprocessable_content
  end
end

#update_image_attributesObject

In-place edit for a single packet image attribute (quantity or checkbox),
routed by params[:source].



273
274
275
276
277
278
279
280
# File 'app/controllers/crm/exported_catalog_item_packets_controller.rb', line 273

def update_image_attributes
  if params[:source] == 'quantity'
    ExportedCatalogItemPacketImageAttribute.find(params[:ia_id]).update(quantity: params[:ia_value])
  elsif params[:source] == 'checkbox'
    ExportedCatalogItemPacketImageAttribute.find(params[:ia_id]).update(selected: params[:ia_check_value])
  end
  head :ok
end