Class: CreditMemosController

Inherits:
CrmController show all
Includes:
Controllers::Destroyable, Controllers::Workflowable
Defined in:
app/controllers/credit_memos_controller.rb

Overview

== Schema Information

Table name: credit_memos

id :integer not null, primary key
customer_id :integer
credit_order_id :integer
original_order_id :integer
total :decimal(8, 2)
currency :string(255)
discount :decimal(8, 2)
line_total :decimal(8, 2)
state :string(255)
primary_sales_rep_id :integer
secondary_sales_rep_id :integer
account_specialist_id :integer
billing_address :text
shipping_address :text
tax_total :decimal(8, 2)
shipping_cost :decimal(8, 2)
reference_number :string(255)
uploads_count :integer
company_id :integer
transmission_state :string(255)
purchase_order_id :integer
rma_id :integer
request_date :date
creator_id :integer
updater_id :integer
created_at :datetime
updated_at :datetime
category :string(255)
local_sales_rep_id :integer
billing_address_id :integer
shipping_address_id :integer
original_order_missing_reason :string(255)
remark :text
original_po_number :string(255)
tax_date :date
gl_date :date
document_date :date
shipping_coupon :decimal(8, 2)
taxable_total :decimal(12, 2)
legacy_taxes :boolean
line_total_discounted :decimal(8, 2)
billing_customer_id :integer
order_type :string(255)

Constant Summary

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

Constants included from Www::SeoHelper

Www::SeoHelper::AWARDS, Www::SeoHelper::CA_ADDRESS, Www::SeoHelper::CA_BUSINESS_HOURS, Www::SeoHelper::CA_CONTACT_POINT, Www::SeoHelper::CA_CURRENCIES, Www::SeoHelper::CA_DESCRIPTION, Www::SeoHelper::CA_FOUNDING_DATE, Www::SeoHelper::CA_GLOBAL_LOCATION_NUMBER, Www::SeoHelper::CA_LEGAL_NAME, Www::SeoHelper::CA_LOCAL_BUSINESS, Www::SeoHelper::CA_ONLINE_STORE, Www::SeoHelper::CA_RETURN_POLICY, Www::SeoHelper::CA_SALES_DEPARTMENT, Www::SeoHelper::CA_SERVICE_AREA, Www::SeoHelper::CA_URL, Www::SeoHelper::CA_VAT_ID, Www::SeoHelper::CA_WAREHOUSE_DEPARTMENT, Www::SeoHelper::CA_WAREHOUSE_HOURS, Www::SeoHelper::COMPANY_EMAIL, Www::SeoHelper::COMPANY_LOGO, Www::SeoHelper::COMPANY_NAME, Www::SeoHelper::COMPANY_SLOGAN, Www::SeoHelper::EXPERTISE, Www::SeoHelper::FAX_NUMBER, Www::SeoHelper::GS1_COMPANY_PREFIX, Www::SeoHelper::ISO6523_CODE, Www::SeoHelper::PAYMENT_METHODS, Www::SeoHelper::PHONE_NUMBER, Www::SeoHelper::PRIMARY_NAICS, Www::SeoHelper::REFUND_TYPE, Www::SeoHelper::RETURN_FEES, Www::SeoHelper::RETURN_METHOD, Www::SeoHelper::RETURN_POLICY_CATEGORY, Www::SeoHelper::SECONDARY_NAICS, Www::SeoHelper::SOCIAL_PROFILES, Www::SeoHelper::US_ADDRESS, Www::SeoHelper::US_BUSINESS_HOURS, Www::SeoHelper::US_CONTACT_POINT, Www::SeoHelper::US_CURRENCIES, Www::SeoHelper::US_DESCRIPTION, Www::SeoHelper::US_FOUNDING_DATE, Www::SeoHelper::US_GLOBAL_LOCATION_NUMBER, Www::SeoHelper::US_IMAGE, Www::SeoHelper::US_LEGAL_NAME, Www::SeoHelper::US_LOCAL_BUSINESS, Www::SeoHelper::US_ONLINE_STORE, Www::SeoHelper::US_RETURN_POLICY, Www::SeoHelper::US_SALES_DEPARTMENT, Www::SeoHelper::US_SERVICE_AREA, Www::SeoHelper::US_TAX_ID, Www::SeoHelper::US_URL, Www::SeoHelper::US_WAREHOUSE_DEPARTMENT, Www::SeoHelper::US_WAREHOUSE_HOURS

Constants included from IconHelper

IconHelper::CUSTOM_ICON_MAP, IconHelper::CUSTOM_SVG_DIR, IconHelper::DEFAULT_FAMILY

Instance Method Summary collapse

Methods included from Controllers::Destroyable

#destroy, #perform_destroy

Methods included from Controllers::Workflowable

#render_workflow_error_stream, #render_workflow_success_stream, #workflow_action, #workflow_action_complete

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, #initialize_crm_lazy_chunks, #record_not_found, #redirect_to_job_or_fallback, #render_edit_action, #set_context, #set_download_path, #stash_file_for_temp_download

Methods inherited from ApplicationController

#account_impersonated?, #add_to_flash, #append_token, #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?, #stamp_impersonation_context, #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, #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::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, #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_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!, #authenticate_account_from_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 ApplicationHelper

#better_number_to_currency, #check_force_logout, #check_or_cross, #check_or_times, #error_messages, #general_disclaimer_on_product_installation_and_local_codes, #gridjs_from_html_table, #gridjs_table, #is_wy_ip, #line_break, #parent_layout, #pass_or_fail, #render_error_messages_list, #render_video_card, #resolved_auth_form_turbo_frame, #return_path_or, #safe_css_color, #set_return_path_if_present, #set_section_if_present, #tab_frame_id, #to_underscore, #track_page?, #turbo_section_wrapper, #turbo_tabs_request?, #url_on_same_domain_as_request, #widget_index_daily_focus_index_path, #working_hours?, #yes_or_no, #yes_or_no_highlighted, #yes_or_no_with_check_or_cross, #youtube_video

Methods included from UppyUploaderHelper

#file_uploader, #image_uploader, #large_file_uploader_s3, #lead_sketch_uploader, #rma_image_uploader, #rma_image_uploader_s3, #uppy_uploader, #video_uploader

Methods included from Www::ImagesHelper

#image_asset_tag, #image_asset_url

Methods included from Www::SeoHelper

#add_page_schema, #canada?, #company_social_links, #ensure_context_json, #json_ld_script_tag, #local_business_schema, #online_store_id, #online_store_schema, #page_main_entity, #page_main_entity_json, #render_auto_collection_page_schema, #render_collection_page_schema, #render_local_business_schema, #render_online_store_schema, #render_page_schemas, #render_page_video_schemas, #render_webpage_schema, #render_webpage_schema_with_collections, #usa?

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?

Methods included from IconHelper

#account_nav_icon, #fa_icon, #star_rating_html

Instance Method Details

#address_lookupObject

GET /credit_memos/address_lookup
Search addresses for tom-select dropdown based on customer_id or invoice_ref



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'app/controllers/credit_memos_controller.rb', line 142

def address_lookup
  q = params[:q]&.strip
  customer_id = params[:customer_id].presence
  invoice_ref = params[:invoice_ref].presence

  addresses = []

  # If invoice_ref is provided, get ONLY the addresses from the invoice
  if invoice_ref.present?
    invoice = Invoice.find_by(reference_number: invoice_ref)
    if invoice
      addresses << invoice.billing_address if invoice.billing_address
      addresses << invoice.shipping_address if invoice.shipping_address
    end
  elsif customer_id.present?
    # Get all addresses for the customer
    customer = Customer.find_by(id: customer_id)
    addresses = customer.all_addresses.to_a if customer
  end

  # Remove duplicates and filter by search term
  addresses = addresses.compact.uniq
  addresses = addresses.select { |a| a.to_s.downcase.include?(q.downcase) } if q.present?

  json_result = {
    results: addresses.map { |a| { id: a.id, text: a.to_s } },
    pagination: { more: false }
  }
  render json: json_result
end

#approveObject



270
271
272
273
274
# File 'app/controllers/credit_memos_controller.rb', line 270

def approve
  @credit_memo = CreditMemo.find(params[:id])
  @credit_memo.approve!
  redirect_to @credit_memo
end

#createObject

POST /credit_memos



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

def create
  @credit_memo = CreditMemo.new(params[:credit_memo])
  @addresses = @credit_memo.customer.nil? ? [] : @credit_memo.customer.all_addresses.to_a
  @invoice = @credit_memo.original_invoice
  if @invoice
    @addresses << @invoice.shipping_address unless @addresses.include?(@invoice.shipping_address)
    @addresses << @invoice.billing_address unless @addresses.include?(@invoice.billing_address)
  end

  if @credit_memo.save
    redirect_to(@credit_memo, info: 'Credit Memo was successfully created.')
  else
    render action: 'new', status: :unprocessable_entity
  end
end

#editObject

GET /credit_memos/1/edit



204
205
206
207
208
209
210
211
212
213
214
215
# File 'app/controllers/credit_memos_controller.rb', line 204

def edit
  @credit_memo = CreditMemo.find(params[:id])
  redirect_to(@credit_memo, error: 'You cannot edit this Credit Memo') and return if @credit_memo.editing_locked?

  @customer = @credit_memo.customer
  @invoice = @credit_memo.original_invoice
  @addresses = @customer.all_addresses.to_a
  return unless @invoice

  @addresses << @invoice.shipping_address unless @addresses.include?(@invoice.shipping_address)
  @addresses << @invoice.billing_address unless @addresses.include?(@invoice.billing_address)
end

#indexObject

GET /credit_memos



57
58
59
# File 'app/controllers/credit_memos_controller.rb', line 57

def index
  @pagy, @credit_memos = pagy(CreditMemo.includes(:rma, :original_order, :credit_order, :customer, :company).order(reference_number: :desc))
end

#invoice_lookupObject

GET /credit_memos/invoice_lookup
Search invoices for tom-select dropdown



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/controllers/credit_memos_controller.rb', line 123

def invoice_lookup
  q = params[:q]&.strip
  customer_id = params[:customer_id].presence

  results = Invoice.includes(:customer).order(id: :desc)
  results = results.where(customer_id: customer_id) if customer_id
  results = results.where('invoices.reference_number ILIKE ?', "%#{q}%") if q.present?

  json_result = TomSelect.format_json_results(self, results, params[:page].presence, params[:per_page].presence || 20) do |inv|
    {
      id: inv.reference_number,
      text: "#{inv.reference_number} - #{inv.customer.try(:full_name)} ($#{inv.total})"
    }
  end
  render json: json_result
end

#newObject

GET /credit_memos/new



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'app/controllers/credit_memos_controller.rb', line 174

def new
  @customer = Customer.find_by(id: params[:customer_id])
  @invoice = Invoice.find_by(id: params[:invoice_id])
  @company = @customer&.catalog&.store&.company
  @currency = @company&.currency
  @addresses = @customer&.all_addresses.to_a
  @credit_memo = CreditMemo.new(customer_id: params[:customer_id],
                                customer_name: @customer&.full_name,
                                original_invoice_ref: @invoice&.reference_number,
                                company_id: @company&.id,
                                currency: @currency,
                                request_date: Date.current,
                                gl_date: Date.current,
                                document_date: Date.current,
                                tax_date: Date.current)
  @credit_memo.line_items.build
  if @invoice
    @addresses << @invoice.shipping_address unless @addresses.include?(@invoice.shipping_address)
    @addresses << @invoice.billing_address unless @addresses.include?(@invoice.billing_address)
    @credit_memo.shipping_address = @invoice.shipping_address
    @credit_memo.billing_address = @invoice.billing_address
    @credit_memo.original_po_number = @invoice.po_number
  elsif @customer
    @credit_memo.shipping_address = @customer.shipping_address
    @credit_memo.billing_address = @customer.billing_address
  end
  @addresses.compact! # remove any nil addresses
end

#pdfObject



286
287
288
289
290
291
# File 'app/controllers/credit_memos_controller.rb', line 286

def pdf
  @credit_memo = CreditMemo.find(params[:id])
  @pdf = @credit_memo.uploads.where(category: 'credit_memo_pdf').first
  @pdf ||= @credit_memo.generate_pdf
  redirect_to upload_path(@pdf)
end

#printedObject



260
261
262
263
264
265
266
267
268
# File 'app/controllers/credit_memos_controller.rb', line 260

def printed
  @credit_memo = CreditMemo.find(params[:id])
  begin
    @credit_memo.printed!
  rescue StandardError => e
    flash[:error] = "Unable to print credit memo, message: #{e}"
  end
  redirect_to @credit_memo
end

#process_refundObject

POST /credit_memos/process_refund



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/controllers/credit_memos_controller.rb', line 94

def process_refund
  @credit_memo = CreditMemo.find(params[:id])
  error = false
  if @credit_memo.processing_refund?
    error = true
    error_message = 'A refund is already being processed for this credit memo'
  elsif params['payments'].nil? or params['payments'].to_h.all? { |_auth_id, attrs| attrs['refund_amount'].blank? or BigDecimal(Normalizr.normalize(attrs['refund_amount'], :currency)) == 0 }
    error = true
    error_message = 'You must specify at least one refund amount'
  else
    auths = {}
    params[:payments].each { |auth_id, attrs| auths[auth_id.to_i] = BigDecimal(Normalizr.normalize(attrs['refund_amount'], :currency)) unless attrs['refund_amount'].blank? or BigDecimal(Normalizr.normalize(attrs['refund_amount'], :currency)) == 0 }
    result = @credit_memo.process_refund(auths)
    if result[:success] == false
      error = true
      error_message = result[:error_message]
    end
  end
  if error == true
    flash.now[:error] = error_message
    render :refund, status: :unprocessable_entity
  else
    flash[:info] = 'Refund was successfully processed'
    redirect_to @credit_memo
  end
end

#refundObject

GET /credit_memos/refund



85
86
87
88
89
90
91
# File 'app/controllers/credit_memos_controller.rb', line 85

def refund
  @credit_memo = CreditMemo.find(params[:id])
  return if @credit_memo.can_be_refunded?

  flash[:error] = 'Credit memo not available to be refunded'
  redirect_to @credit_memo
end

#regenerate_pdfObject



276
277
278
279
280
281
282
283
284
# File 'app/controllers/credit_memos_controller.rb', line 276

def regenerate_pdf
  @credit_memo = CreditMemo.find(params[:id])
  @pdf = @credit_memo.generate_pdf

  respond_to do |format|
    format.turbo_stream { render turbo_stream: turbo_stream.open_url(upload_url(@pdf)) }
    format.html { redirect_to upload_path(@pdf) }
  end
end

#send_edi_messageObject



299
300
301
302
303
# File 'app/controllers/credit_memos_controller.rb', line 299

def send_edi_message
  @credit_memo = CreditMemo.find(params[:id])
  Edi::CreditMemoEventProcessor.new.process(@credit_memo, :created)
  redirect_to(@credit_memo, info: 'Credit memo 810 EDI Message was queued for processing and transmission.')
end

#showObject

GET /credit_memos/1



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'app/controllers/credit_memos_controller.rb', line 62

def show
  @credit_memo = CreditMemo.find(params[:id])
  @show_kits_and_sn = params[:show_kits_and_sn].to_b
  @attachments = @credit_memo.uploads
  @pagy_activities, @activities = pagy(@credit_memo.activities.natural_order)

  set_context(@credit_memo)

  respond_to do |format|
    format.html # show.html.erb
    format.pdf do
      store = @credit_memo.original_order&.store || @credit_memo.company.stores.first
      pdf = CreditMemo::PdfGenerator.new(@credit_memo, store: store, show_tax_info: true, show_kits_and_sn: @show_kits_and_sn).generate

      send_data pdf,
                filename: "#{@credit_memo.file_name}.pdf",
                type: 'application/pdf',
                disposition: 'inline'
    end
  end
end

#tab_account_ledgerObject



335
336
337
338
339
# File 'app/controllers/credit_memos_controller.rb', line 335

def 
  @credit_memo = CreditMemo.find(params[:id])
  authorize!(:read, @credit_memo)
  render layout: should_render_layout?
end

#tab_attachmentsObject



311
312
313
314
315
# File 'app/controllers/credit_memos_controller.rb', line 311

def tab_attachments
  @credit_memo = CreditMemo.find(params[:id])
  authorize!(:read, @credit_memo)
  render layout: should_render_layout?
end

#tab_line_itemsObject



305
306
307
308
309
# File 'app/controllers/credit_memos_controller.rb', line 305

def tab_line_items
  @credit_memo = CreditMemo.find(params[:id])
  authorize!(:read, @credit_memo)
  render layout: should_render_layout?
end

#tab_outgoing_paymentsObject



323
324
325
326
327
# File 'app/controllers/credit_memos_controller.rb', line 323

def tab_outgoing_payments
  @credit_memo = CreditMemo.find(params[:id])
  authorize!(:read, @credit_memo)
  render layout: should_render_layout?
end

#tab_paymentsObject



317
318
319
320
321
# File 'app/controllers/credit_memos_controller.rb', line 317

def tab_payments
  @credit_memo = CreditMemo.find(params[:id])
  authorize!(:read, @credit_memo)
  render layout: should_render_layout?
end

#tab_receiptsObject



329
330
331
332
333
# File 'app/controllers/credit_memos_controller.rb', line 329

def tab_receipts
  @credit_memo = CreditMemo.find(params[:id])
  authorize!(:read, @credit_memo)
  render layout: should_render_layout?
end

#transmitObject



293
294
295
296
297
# File 'app/controllers/credit_memos_controller.rb', line 293

def transmit
  @credit_memo = CreditMemo.find(params[:id])
  redir_params = CommunicationBuilder.new(resource: @credit_memo, current_user: current_user).to_params
  redirect_to new_communication_path(redir_params.merge({ return_path: credit_memo_path(@credit_memo) }))
end

#updateObject

PUT /credit_memos/1



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'app/controllers/credit_memos_controller.rb', line 235

def update
  @credit_memo = CreditMemo.find(params[:id])
  redirect_to(@credit_memo, error: 'You cannot edit this Credit Memo') and return if @credit_memo.editing_locked?

  tax_exempt = params[:credit_memo][:tax_exempt].to_i
  if tax_exempt.positive?
    resource_tax_rate = @credit_memo.resource_tax_rate
    @credit_memo.update(tax_exempt: true, resource_tax_rate_id: nil)
    resource_tax_rate.destroy
  end
  @customer = @credit_memo.customer
  @invoice = @credit_memo.original_invoice
  @addresses = @customer.all_addresses.to_a
  if @invoice
    @addresses << @invoice.shipping_address unless @addresses.include?(@invoice.shipping_address)
    @addresses << @invoice.billing_address unless @addresses.include?(@invoice.billing_address)
  end

  if @credit_memo.update(params[:credit_memo])
    redirect_to(@credit_memo, info: 'Credit memo was successfully updated.')
  else
    render action: 'edit', status: :unprocessable_entity
  end
end